I would like to exclude some parameters (e.g. city, userId, app role, ...) from traces and dependencies logs in Application Insights.
Have you any suggestions?
You can use ITelemetryInitializer to change the value of userId / app role to empty. Then in the AI logs, city, userId, app role will be removed automatically.
I test it with a .net core web app:
1.Add a MyTelemetryInitializer class to the web project:
public class MyTelemetryInitializer: ITelemetryInitializer
{
public void Initialize(ITelemetry telemetry)
{
//only apply for Trace and Dependency
if (telemetry is TraceTelemetry || telemetry is DependencyTelemetry)
{
telemetry.Context.User.Id = "";
telemetry.Context.Cloud.RoleInstance = "";
telemetry.Context.Cloud.RoleName = "";
}
}
}
2.In the Startup.cs -> ConfigureServices method, register the custom telemetry initializer:
public void ConfigureServices(IServiceCollection services)
{
//other code
services.AddSingleton<ITelemetryInitializer, MyTelemetryInitializer>();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
3.In the HomeController.cs, create a TelemetryClient to send telemetry data:
public class HomeController : Controller
{
TelemetryClient client = new TelemetryClient();
public IActionResult Index()
{
client.TrackTrace("0624-2 index: this is a trace message!!!");
return View();
}
public IActionResult About()
{
client.TrackTrace("0624-2 about: this is a trace message!!!");
ViewData["Message"] = "Your application description page.";
return View();
}
public IActionResult Contact()
{
client.TrackTrace("0624-2 contact: this is a trace message!!!");
ViewData["Message"] = "Your contact page.";
return View();
}
}
4.After running the project, wait for a few minutes, then nav to azure portal -> your app insights -> search page, check the properties of the trace messages:
Related
I have an azure function, it logs the information without any issues.
namespace aspnetcore_azurefun_blob
{
[StorageAccount("AzureWebJobsStorage")]
public class FileTrigger
{
#region Property
private readonly IFileProcessor fileProcessor;
#endregion
#region Constructor
public FileTrigger(IFileProcessor fileProcessor)
{
this.fileProcessor = fileProcessor;
}
#endregion
[FunctionName("FileTrigger")]
public void ProcessFilesFromSamplesContainer([BlobTrigger("samples-workitems/{name}")]Stream myBlob, string name, ILogger log, ExecutionContext context)
{
log.LogInformation("Function: ProcessFilesFromSamplesContainer is called");
log.LogInformation($"C# Blob trigger function Processed blob\n Name:{name} \n Size: {myBlob.Length} Bytes");
var result = fileProcessor.ProcessAsync(myBlob, name);
log.LogInformation($"Function Completed Successfully on {DateTime.Now.ToLongDateString()} # {DateTime.Now.ToShortTimeString()}.\n.");
}
However, I also have the business logic implemented using DI and below is the excerpt of the implementation.
ServiceBase.cs
namespace BusinessService.Services.Common
{
public abstract class ServiceBase<T>
{
public static AppDbContext AppDbContext;
public static ILogger<T> Logger { get; set; }
public static AppConfigurator Configurator { get; set; }
public ServiceBase(AppDbContext appDbContext, ILogger<T> logger, IOptions<AppConfigurator> configurator)
{
AppDbContext = appDbContext ?? throw new ArgumentNullException(nameof(appDbContext));
Logger = logger ?? throw new ArgumentNullException(nameof(logger));
Configurator = configurator.Value ?? throw new ArgumentNullException(nameof(configurator));
}
}
}
FileProcessingService.cs
namespace BusinessService.Services
{
public interface IFileProcessingService
{
void Process(Stream myBlob, string name);
}
public class FileProcessingService : ServiceBase<FileProcessingService>, IFileProcessingService
{
#region Constructor
public FileProcessingService(AppDbContext appDbContext, ILogger<FileProcessingService> logger, IOptions<AppConfigurator> configurator)
: base(appDbContext, logger, configurator) { }
#endregion
#region Public Methods
public void Process(Stream myBlob, string name)
{
AppDbContext.FileRecords.Add(new FileRecords
{
FileName = name,
IsCompleted = DefaultValues.IsCompleted
});
AppDbContext.SaveChanges();
Logger.LogInformation("Reading configuration from the configuration settings file: {Configurator.AzureSQLServerConfigurator.ConnnectionString}");
Logger.LogInformation("Database is updated..!");
}
#endregion
}
}
Line#34 and #35 doesn't Log anything
Logger.LogInformation("Reading configuration from the configuration settings file: {Configurator.AzureSQLServerConfigurator.ConnnectionString}");
Logger.LogInformation("Database is updated..!");
DependencyRegistrar.cs
namespace CrossCutting.DependencyInjection
{
public static class DependencyRegistrar
{
public static void Intialize(this IServiceCollection services)
{
// Initialize App Settings from Configurator Settings Json file
services.AddOptions<AppConfigurator>()
.Configure<IConfiguration>((settings, configuration) =>
{
configuration.GetSection("AppConfigurator").Bind(settings);
})
.Validate((c) =>
{
return !new[] { c.AzureSQLServerConfigurator.ConnnectionString }.Any(s => String.IsNullOrWhiteSpace(s));
});
}
}
}
What am I missing so that FileProcessingService.cs will log the information ?
I have checked your code in our end I could get the logging information. In your code i have noticed in your code you are using the Logger instead of logger.
Because in your Region Constructor you are using ILogger<FileProcessingService> logger from here you have to call the logger to push your logging information into Application Insights/Function execution panel (Output console window)
#region Constructor
public FileProcessingService(AppDbContext appDbContext, ILogger<FileProcessingService> logger, IOptions<AppConfigurator> configurator)
: base(appDbContext, logger, configurator) { }
#endregion
#region Public Methods
public void Process(Stream myBlob, string name)
{
AppDbContext.FileRecords.Add(new FileRecords
{
FileName = name,
IsCompleted = DefaultValues.IsCompleted
});
AppDbContext.SaveChanges();
# changed Logger into logger
logger.LogInformation("Reading configuration from the configuration settings file: {Configurator.AzureSQLServerConfigurator.ConnnectionString}");
logger.LogInformation("Database is updated..!");
}
#endregion
Still if not able to push the logs you can add your namespace in your host.json file to avoid missing the logging
{
"version": "2.0",
"logging": {
"logLevel": {
// Here you can use your Project namespace like BusinessService.Services
"<namespace>": "Information"
}
}
}
Using ServiceStack (v 5.5.0) I read the recommended approach to calling services via a controller is by using the Gateway.
Full example is found at https://github.com/RhysWilliams647/ServiceStackControllerTest
public class HomeController : ServiceStackController
{
public ActionResult Index()
{
var response = Gateway.Send<TestServiceResponse>(new TestServiceRequest());
IndexModel model = new IndexModel { Message = response.Message };
return View(model);
}
public ActionResult About()
{
ViewBag.Message = "Your application description page.";
return View();
}
public ActionResult Contact()
{
ViewBag.Message = "Your contact page.";
return View();
}
}
However when testing my controller via xUnit I get a null exception error as Gateway is null. Below is my AppHost
public class AppHost : AppSelfHostBase
{
public AppHost() : base("Test", typeof(TestService).Assembly)
{
}
public override IServiceGateway GetServiceGateway(IRequest req) =>
base.GetServiceGateway(req ?? new BasicRequest());
public override void Configure(Container container)
{
SetConfig(new HostConfig
{
HandlerFactoryPath = "api"
});
container.RegisterFactory<HttpContext>(() => HttpContext.Current);
// register container for mvc
ControllerBuilder.Current.SetControllerFactory(new FunqControllerFactory(container));
}
}
And my test
[Trait("Category", "Controllers")]
[Collection("AppHostFixture")]
public class ControllerTest
{
[Fact]
public void CanCallHomeControllerIndex()
{
var controller = new HomeController();
controller.Index();
}
}
Can someone please advise how to test a ServiceStackController calling the service gateway?
AppSelfHostBase on .NET Framework is a HttpListener self-host which doesn't support MVC so you're not going to be able to run any integration tests.
When you new an MVC controller instance like this:
var controller = new HomeController();
It's base.HttpContext required by ServiceStackController doesn't exist, it requires a ASP.NET HttpContext but the self-host in unit test is running on a self-hosted HttpListener server.
You could try accessing the Gateway via the HostContext singleton i.e:
var gateway = HostContext.AppHost.GetServiceGateway(new BasicRequest());
var response = gateway.Send<TestServiceResponse>(new TestServiceRequest());
In this example it calls the Gateway with a mock Http Request Context to simulate a request, but you wont be able to execute real MVC integration tests with a self-hosted Http Listener.
Is it possible to connect to a Service Bus Topic using App Service Function App's MSI? Any documentations or examples to perform this via ARM Template or .NET SDK (C#) would be helpful.
See example below:
namespaces:
using Microsoft.Azure.ServiceBus;
using Microsoft.Azure.ServiceBus.Primitives;
Adding services to the container:
services.Configure<ServiceBusConfiguration>(Configuration.GetSection("ServiceBus"));
Main implementation:
public class ServiceBusConfiguration
{
public string Namespace { get; set; }
public string Topic { get; set; }
}
public class HomeController : Controller
{
public ServiceBusConfiguration Config { get; }
public HomeController(IOptions<ServiceBusConfiguration> serviceBusConfig)
{
Config = serviceBusConfig.Value;
}
[HttpPost]
public async Task<ActionResult> Send(ServiceBusMessageData messageInfo)
{
if (string.IsNullOrEmpty(messageInfo.MessageToSend))
{
return RedirectToAction("Index");
}
var tokenProvider = TokenProvider.CreateManagedServiceIdentityTokenProvider();
var sendClient = new TopicClient($"sb://{Config.Namespace}.servicebus.windows.net/", Config.Topic, tokenProvider);
await sendClient.SendAsync(new Message(Encoding.UTF8.GetBytes(messageInfo.MessageToSend)));
await sendClient.CloseAsync();
return RedirectToAction("Index");
}
}
To get more details please see Use Service Bus from App Service with Managed Service Identity
Is there a way to configure my Azure Cloud storage in ConfigureServices of the Startup class, the same way I configure my DatabaseContext?
var connection = Configuration.GetConnectionString("myDatabaseConnectionString");
services.AddDbContext<DatabaseContext>
(options => options.UseSqlServer(connection));
I know I could access the connection string from within a controller by adding the IConfiguration instance to the service collection as a singleton object in ConfigureServices and then inject IConfiguration in the controller.
But how do I access this in the following class or is this a bad practice and there is a better way (e.g. in the services.Add ...) ?
public class ClassifiedsToProcess
{
public static void AddMessage(string message)
{
var storageConnectionString = Configuration.Get("AppSettings:ConnectionString");
storageAccount = CloudStorageAccount.Parse(storageConnectionString);
...
}
}
Thanks !
Here is how I solved my problem using IOptions as suggested by Martin Brandl
Controller code
private string makeJson;
private MyAzureQueue MyAzureQueue;
public AdminController(IOptions<AzureOptions> optionsAccessor)
{
MyAzureQueue = new MyAzureQueue(optionsAccessor);
}
public IActionResult Index()
{
MyAzureQueue.AddMessage("my message");
return View();
}
MyAzureQueue class
public interface IMessageRepository
{
void AddMessage(string message);
}
public class MyAzureQueue: IMessageRepository
{
private readonly CloudQueue _queue;
public ClassifiedsToProcess(IOptions<AzureOptions> optionsAccessor)
{
CloudStorageAccount storageAccount = AzureStorageHelper.getStorageAccount(optionsAccessor.Value.StorageConnectionString);
_queue = AzureStorageHelper.GetQueue(storageAccount, "mystorageaccount");
}
public void AddMessage(string message)
{
CloudQueueMessage cloudQueueMessage = new CloudQueueMessage(message);
_queue.AddMessageAsync(cloudQueueMessage);
}
}
Azure Options class
public class AzureOptions
{
public AzureOptions()
{
}
public string StorageConnectionString { get; set; }
}
In the Startup.cs ConfigureServices method I added the following two lines
services.AddOptions();
services.Configure<AzureOptions>(Configuration.GetSection("Azure"));
And this is what my appsettings.json looks like
{
"Azure": {
"StorageConnectionString": "DefaultEndpointsProtocol=https;AccountName=mystorageaccount;AccountKey=XXXXXXXXXXXXXXXXXXXXX;EndpointSuffix=core.windows.net"
}
}
You should try to avoid using IConfiguration directly and use the Option pattern in
ASP.NET Core instead.
In your example, I would e. g. implement an IMessageRepository which uses the storage account to persist the messages.
Your ClassifiedsToProcess class would have a constructor dependency to it and your AddMessage method would used it (it shouldn't be static since you want to access the repository).
I am using visual studio 2013 and created a default mvc project.In home controller I have modified the below
[RoutePrefix("home")]
[Route("{action=index}")]
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
//[Route("Home/About")]
public ActionResult About()
{
ViewBag.Message = "Your application description page.";
return View();
}
public ActionResult Contact()
{
ViewBag.Message = "Your contact page.";
return View();
}
}
In RouteConfig.cs I have done the following modification
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapMvcAttributeRoutes();
//routes.MapRoute(
// name: "Default",
// url: "{controller}/{action}/{id}",
// defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
//);
}
}
This is not working.But when I go for convention based routing it is working. Plz help me.
to make a web api create a WebApiConfig.cs :
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
}
}
}
in your global.asax :
GlobalConfiguration.Configure(WebApiConfig.Register);
and then you can define controller :
[RoutePrefix("home")]
public class HomeController : ApiController
{
[Route("action")]
public string myaction()
{
return "hello world";
}
}
this way the route /home/action will send back the defined string
now the big difference between WebApi and MVC is that you dont use your server side to Template and provide views, it is mainly used for REST actions: GET PUT POST DELETE
I don't know what you want to achieve but I hope this will be usefull
Edit:
I was thinking maybe you forgot to register your MVC routes in global.asax there should be a similar call to make like:
GlobalConfiguration.Configure(RegisterRoutes.routes);
something like that
and in your code try to change :
[RoutePrefix("Home")]
public class HomeController : Controller
{
[Route("Index")]
public ActionResult Index()
{
return View();
}
[Route("About")]
public ActionResult About()
{
ViewBag.Message = "Your application description page.";
return View();
}
public ActionResult Contact()
{
ViewBag.Message = "Your contact page.";
return View();
}
}
and routes should be /Home/Index and /Home/About