In my Web Api App, I have the controller:
[Route("api/[controller]")]
public class ValuesController : Controller
{
// GET api/values
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/values/5
[HttpGet("{id}")]
public string Get(int id)
{
System.Diagnostics.Trace.TraceInformation("in get...");
return "value";
}
}
I want to be able to see the log "in get..." on Azure when I publish it. On Azure, in the App Service Logs I turn on Application Logging (Filesystem) and set the level to Information. In the Log Stream, when I go to the url of the method, I see in the logs:
Connecting... 2020-02-09T06:07:38 Welcome, you are now connected to
log-streaming service. The default timeout is 2 hours. Change the
timeout with the App Setting SCM_LOGSTREAM_TIMEOUT (in seconds).
2020-02-09 06:08:07.158 +00:00 [Information]
Microsoft.AspNetCore.Hosting.Internal.WebHost: Request starting
HTTP/1.1 GET
http://testapi20200208104448.azurewebsites.net/api/values/5 2020-02-09
06:08:07.159 +00:00 [Information]
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker: Executing
action method TestApi.Controllers.ValuesController.Get (TestApi) with
arguments (5) - ModelState is Valid 2020-02-09 06:08:07.160 +00:00
[Information] Microsoft.AspNetCore.Mvc.Internal.ObjectResultExecutor:
Executing ObjectResult, writing value
Microsoft.AspNetCore.Mvc.ControllerContext.
But I don't see my log "in get..."
This is a known issue for .NET core (but for .NET framework, it can work well).
As a workaround for .NET core web application, I suggest you can use ILogger, which can write message to Application Logs.
In Startup.cs -> Configure method, re-write Configure method like below:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
//your other code
//add the following 2 lines of code.
loggerFactory.AddConsole();
loggerFactory.AddDebug();
app.UseStaticFiles();
//your other code
}
then in ValuesController:
public class ValuesController : Controller
{
private readonly ILogger _logger;
public ValuesController(ILoggerFactory loggerFactory)
{
_logger = loggerFactory.CreateLogger<ValuesController>();
}
// GET api/values
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/values/5
[HttpGet("{id}")]
public string Get(int id)
{
//System.Diagnostics.Trace.TraceInformation("in get...");
//use ILogger here.
_logger.LogInformation("in get...");
return "value";
}
}
Ivan's answer did not fully work for me on aspnetcore 3.1, but it led me to this article, which gave me all information I needed:
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/logging/?view=aspnetcore-3.1
Related
I am testing my deployed Azure function and getting the following error. My function runs locally connecting to Azure database but fails when its deployed and run. I have configured the application settings to read the secret url to the connection string.
This is how my connectionstring looks like
Server=tcp:ranjitazuredb.database.windows.net,1433;Initial Catalog=Srl;Persist Security Info=False;User ID=usr;Password=pwd;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;
Application setting - Url to the secret
https://srlcustomermanagervault.vault.azure.net/secrets/ConnectionString
Function
public class GetCustomersOrders
{
private readonly ICustomerOrdersRepository _repo;
private readonly IMapper _mapper;
private readonly TelemetryClient _telemetryClient;
public GetCustomersOrders(ICustomerOrdersRepository repo, IMapper mapper, TelemetryConfiguration configuration)
{
_repo = repo;
_mapper = mapper;
_telemetryClient = new TelemetryClient(configuration);
}
[FunctionName("GetCustomersOrders")]
public async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", Route = "customer-orders")] HttpRequest req,
ILogger log)
{
this._telemetryClient.TrackTrace("C# HTTP trigger function processed a request.");
var customersOrders = _repo.GetCustomerOrders();
return new OkObjectResult(_mapper.Map<List<CustomerOrdersViewModel>>(customersOrders));
}
}
This is how I have assigned the policy
Function start up
[assembly: FunctionsStartup(typeof(Startup))]
namespace SRL.CustomerOrder
{
internal class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
var connectionString = Environment.GetEnvironmentVariable("ConnectionString");
builder.Services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
builder.Services.AddScoped<ISrlContext, CustomerManagerContext>();
builder.Services.AddAutoMapper(typeof(Startup));
builder.Services.AddDbContext<CustomerManagerContext>(options =>
options.UseSqlServer(connectionString));
builder.Services.AddTransient<ICustomerDetailsRepository, CustomerDetailsRepository>();
builder.Services.AddTransient<ICustomerOrdersRepository, CustomerOrdersRepository>();
builder.Services.AddTransient<IOrderDetailsRepository, OrderDetailsRepository>();
}
}
}
Presuming the connection string worked when you used it directly in the app settings I would check out this link
https://learn.microsoft.com/en-us/azure/app-service/app-service-key-vault-references
So in your example you would use
#Microsoft.KeyVault(SecretUri=https://srlcustomermanagervault.vault.azure.net/secrets/ConnectionString
)
The documentation says you need the version id but you do not, (it is a bug that it works). Azure is working on a release so that it works without a version which should probably be out in preview by now and if not shortly. I have talked with several people and have it working for a client without the version.
I have an Application Insights which logs traces from an App Service and an App Function (one resource for 2 functions).
I need to filter traces according to the resource (App Service or App Function) and, if possible, for the App Function which function is actually logging.
Looking at the traces I see the following list of properties:
I thought to find the resource name in the appName property, instead there is the Application Insights resource name, which is useless for me, since all those traces are from that resource.
Note: I don't like the workaround to set a prefix in the message to filter the traces.
UPDATE
I followed Peter Bons suggestions and I created a brand new Function V3 project. The basic version of the project worked also without the Telemetry Initializer, I mean that the Cloud_RoleName property was correctly populated.
Then, I added my changes to adapt the sample code and I found that the problem comes up when I inject a new Telemetry Client. I know, it is not recommended to manually inject TelemetryClient in App Function, but I absolutely need to send Custom Event to Application Insights and, as far as I know, it is not possible with ILogger interface used by default in App Function.
Startup.cs
public class Startup : FunctionsStartup
{
private TelemetryConfiguration telemetryConfiguration;
public override void Configure(IFunctionsHostBuilder builder)
{
var localRoot = Environment.GetEnvironmentVariable("AzureWebJobsScriptRoot");
var azureRoot = $"{Environment.GetEnvironmentVariable("HOME")}/site/wwwroot";
var configBuilder = new ConfigurationBuilder()
.SetBasePath(localRoot ?? azureRoot)
.AddEnvironmentVariables()
.AddJsonFile("local.settings.json", optional: true, reloadOnChange: true);
var configuration = configBuilder.Build();
if (builder != null)
{
this.ConfigureServices(builder.Services, configuration);
}
}
private void ConfigureServices(IServiceCollection services, IConfiguration configuration)
{
services.AddSingleton<ITelemetryInitializer>(x => new CustomTelemetryInitializer(configuration["appFunctionName"]));
telemetryConfiguration = new TelemetryConfiguration(configuration["APPINSIGHTS_INSTRUMENTATIONKEY"]);
telemetryConfiguration.TelemetryInitializers.Add(new OperationCorrelationTelemetryInitializer());
var telemetryClient = new TelemetryClient(telemetryConfiguration);
services.AddSingleton(telemetryClient);
services.AddSingleton<ISampleInterface, SampleService>();
}
}
CustomTelemetryInitializer.cs
public class CustomTelemetryInitializer : ITelemetryInitializer
{
private readonly string roleName;
public CustomTelemetryInitializer(string roleName)
{
this.roleName = roleName;
}
public void Initialize(ITelemetry telemetry)
{
if (string.IsNullOrEmpty(telemetry?.Context?.Cloud?.RoleName))
{
telemetry.Context.Cloud.RoleName = roleName;
}
}
}
SampleService.cs
public class SampleService : ISampleInterface
{
private TelemetryClient telemetryClient;
public SampleService(TelemetryClient telemetryClient)
{
this.telemetryClient = telemetryClient;
}
public void TestAppInsights()
{
telemetryClient.TrackEvent("Sample Custom Event with init");
telemetryClient.TrackTrace("Sample Custom Trace with init");
}
}
Function.cs
public class Function1
{
private ISampleInterface service;
public Function1(ISampleInterface service)
{
this.service = service;
}
[FunctionName("Function1")]
public async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request with init.");
this.service.TestAppInsights();
string name = req.Query["name"];
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
dynamic data = JsonConvert.DeserializeObject(requestBody);
name = name ?? data?.name;
string responseMessage = string.IsNullOrEmpty(name)
? "This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response."
: $"Hello, {name}. This HTTP triggered function executed successfully.";
return new OkObjectResult(responseMessage);
}
}
How about inspecting the cloud_RoleName property, available to all telemetry? By default it will have the name of the webapp or function (including slot names) as the value.
Otherwise, if you want to add custom properties or modify properties for all telemetry at one place you can make use of a telemetry initializer as demonstrated here:
using Microsoft.ApplicationInsights.Channel;
using Microsoft.ApplicationInsights.Extensibility;
namespace CustomInitializer.Telemetry
{
public class MyTelemetryInitializer : ITelemetryInitializer
{
public void Initialize(ITelemetry telemetry)
{
telemetry.Context.Cloud.RoleName = "HttpTriggered";
}
}
}
This avoids having to prefix all traces as you mentioned as a work around by having a single piece of code all telemetry passes through:
Another thing
[...] but I absolutely need to send Custom Event to Application Insights and, as far as I know, it is not possible with ILogger interface used by default in App Function.
Do note that you can redirect the output emitted by using the ILogger interface to Application Insights. It will show up as a trace.
Recently we encountered an issue where our servers times out because of huge traffic surge, and those request telemetries were logged into AI as success with response code zero. Is there any way to configure response code zero to be termed as failure. Since request telemetries are captured automatically by AI so we dont have any handle on that
You can do it by using ITelemetryInitializer in your .NET core project.
To be termed as failure when response code is zero, you can set the Success property of request telemetry data as false. The sample code as below(using .NET core 2.2 for this test). And please make sure you're using the latest version of Microsoft.ApplicationInsights.AspNetCore 2.13.1.
Here is the custom ITelemetryInitializer:
public class MyTelemetryInitializer : ITelemetryInitializer
{
public void Initialize(ITelemetry telemetry)
{
if (telemetry is RequestTelemetry)
{
var requestTelemetry = telemetry as RequestTelemetry;
//you can change the ResponseCode to "0" in your project
if (requestTelemetry.ResponseCode == "200")
{
// set Success property to false
requestTelemetry.Success = false;
}
}
}
}
then register it in the Startup.cs -> ConfigureServices method:
public void ConfigureServices(IServiceCollection services)
{
//your other code
//here, register the custom ITelemetryInitializer
services.AddSingleton<ITelemetryInitializer, MyTelemetryInitializer>();
}
After executing the code, in azure portal -> your application insights -> Logs, you can see the Success property of request are made as false:
I implemented TelemetryClient to sink application logs into Application Insight. Below is my implementation
public class Log : ILog
{
private static TelemetryClient telemetryClient = new TelemetryClient() { InstrumentationKey = ConfigurationManager.AppSettings["APPINSIGHTS_INSTRUMENTATIONKEY"] };
public void Error(string message, Exception ex = null)
{
telemetryClient.TrackTrace(message, SeverityLevel.Error);
if (ex != null)
telemetryClient.TrackException(ex);
}
public void Info(string message)
{
telemetryClient.TrackTrace(message, SeverityLevel.Information);
}
public void Verbose(string message)
{
telemetryClient.TrackTrace(message, SeverityLevel.Verbose);
}
public void Warning(string message)
{
telemetryClient.TrackTrace(message, SeverityLevel.Warning);
}
public TelemetryClient TelemetryClient
{
get
{
return telemetryClient;
}
}
}
I could see all custom logs in ApplicationInsight
Problem
When I want to do live monitoring, I could not see custom logs in Azure function logs window, function display below log which I'm not writing.
It's expected. The Azure function logs window you mentioned shows logs written in functions by TraceWriter or ILogger, including those logs created by function runtime, i.e. the function execution results you saw.
Using this Track* method, telemetryClient sends telemetries for display in Diagnostic Search, which are not available in function log streaming.
If you want see those Track* in function logs, output them using TraceWriter or ILogger(recommended) and configure log level in host.json.
I am working on a .Net Core web application and we would like to be able to redirect a type of url to our custom error page. The site is hosted on Azure and it seems that this error is not being handled in the application. Here is the type of URL I am working with:
www.mywebsite.com/%22http://www.yahoo.com/%22
The error page that is presented is the following:
The page cannot be displayed because an internal server error has occurred.
In addition when I check the live HTTP traffic on Azure it does not show an error occurring.
Edit:
Apparently azure cannot handle this type request at all: https://azure.microsoft.com/%22/http://www.google.com/
It looks for the config file within the second url. Does anyone know where I can file a bug with Microsoft?
I haven't tested this on Azure, however it works on our server.
Configure exceptions handling in Startup class.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
// ...
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/error");
}
// ...
app.UseStatusCodePagesWithRedirects("/error/{0}");
// ...
}
And error controller.
[Route("[controller]")]
public class ErrorController : Controller
{
[Route("")]
public IActionResult Index()
{
return View();
}
[Route("{httpStatusCode}")]
public IActionResult Index(int httpStatusCode)
{
// handle by error code
switch (httpStatusCode)
{
case (int)HttpStatusCode.NotFound:
return View("NotFound");
default:
return View();
}
}
}