I may me missing something basic here - but is it possible to retrieve the HttpContext.Current in a custom NLog event?
I am trying to give each request a unique Guid so that I can correlate logging messages to a single event (i.e, tie together each log event for a single request). So, I want to store this Guid in HttpContext.Current.Items, then retrieve it in the NLog target and include it in the log message.
Here is my example target where I'd like to access HttpContext.Current:
[Target("AzureTableTarget")]
public class AzureTableTarget : TargetWithLayout
{
public AzureTableTarget()
{
_appSettings = IoCResolver.Get<IAppSettings>();
}
protected override void Write(LogEventInfo logEvent)
{
var correlationId = HttpContext.Current; //This is always null
var batchOperation = new TableBatchOperation();
CxLogEventBuilder.Build(_appSettings, logEvent).ForEach(batchOperation.Insert);
_loggingTable.ExecuteBatchAsync(batchOperation);
}
}
Nowadays it's easier to retrieve the HTTP Context in a NLog target (works for ASP.NET and ASP.NET Core)
Install NLog.Web (ASP.NET) or NLog.Web.AspNetCore (ASP.NET Core) package
For ASP.NET core, follow the ASP.NET Core - NLog setup
Inherit from AspNetLayoutRendererBase (namespace NLog.Web.LayoutRenderers)
Get the request by calling var context = HttpContextAccessor.HttpContext;
Example:
[LayoutRenderer("aspnet-sessionid")]
[ThreadSafe]
public class AspNetSessionIdLayoutRenderer : AspNetLayoutRendererBase
{
protected override void DoAppend(StringBuilder builder, LogEventInfo logEvent)
{
var context = HttpContextAccessor.HttpContext;
var contextSession = context?.Session();
if (contextSession == null)
{
InternalLogger.Debug("HttpContext Session Lookup returned null");
return;
}
builder.Append(contextSession.SessionID); // ASP.NET Core: contextSession.Id
}
}
PS: there are currently many predefined renderers for ASP.NET (Core): https://nlog-project.org/config/?tab=layout-renderers&search=aspnet
If your custom target should capture one (or more) context-specific values, then I recommend that your target inherits from TargetWithContext (or AsyncTaskTarget).
It gives the ability to setup and capture contextproperty-items. Where the Layout can be assigned to capture context-details. Examples of possible context-details easily available from HttpContext:
https://nlog-project.org/config/?tab=layout-renderers&search=package:nlog.web.aspnetcore
For more details about writing custom-targets:
https://github.com/NLog/NLog/wiki/How-to-write-a-custom-target-for-structured-logging
https://github.com/NLog/NLog/wiki/How-to-write-a-custom-async-target
Btw. there already exists this custom target that nicely inherits from AsyncTaskTarget:
https://www.nuget.org/packages/NLog.Extensions.AzureCosmosTable/
This article about Working with HttpContext.Current might help. The key, for you, might be that when control passes from one thread to another HttpContext.Current in the new thread can be null.
Here is another question/answer from here on SO that describes HttpContext.Current being null in the context of a web service. The accepted answer suggests turning on ASP.Net compatibility in your web.config file.
I don't know of either of these will help, but they might. I found them by googling for "HttpContext.Current is null", which yielded quite a number of hits. I have done very little ASP.NET development, so I can't really comment on HttpContext.Current from my own personal experience.
Given your use case, I would suggest that you look into System.Diagnostics.CorrelationManager.ActivityId.
One nice feature of ActivityId is that it is "flowed" from parent threads to child threads (including thread pool threads). I think that it works well with Tasks and Parallel operations. Works well meaning that the ActivityId, as set in a parent thread, has the expected value in a child thread.
There is not a LayoutRenderer for ActivityId, but it easy enough to write one. See an example (written against NLog 1.0) here:
Most useful NLog configurations
I'm pretty sure that the "EstimatedBufferSize" stuff is no longer needed, so something like will probably work:
[LayoutRenderer("ActivityId")]
class ActivityIdLayoutRenderer : LayoutRenderer
{
protected override void Append(StringBuilder builder, LogEventInfo logEvent)
{
builder.Append(Trace.CorrelationManager.ActivityId);
}
}
If you go this route, you might consider adding a Format property to the ActivityIdLayoutRenderer to allow you to specify the guid format. See this answer (from me). It contains a lot of useful information about working with guids.
NewGuid vs System.Guid.NewGuid().ToString("D");
See this source file (in NLog's git repository) for an example of how you can implement and use such a Format property:
https://github.com/NLog/NLog/blob/master/src/NLog/LayoutRenderers/GuidLayoutRenderer.cs
Related
I am using .NET Core 3.1. I have the following code snippet in Startup.cs inside ConfigureServices:
public void ConfigureServices(IServiceCollection services)
{
// ...
services.AddDataProtection()
.SetApplicationName("MyApplication")
.PersistKeysToFileSystem(new DirectoryInfo(Path.Combine("AppData", "Keys")));
// ...
}
As far as I understand, this is only needed if I want to protect some input with IDataProtectionProvider, like so:
public class MyClass
{
readonly IDataProtectionProvider _rootProvider;
public MyClass(IDataProtectionProvider rootProvider)
{
_rootProvider = rootProvider;
}
public void Test()
{
IDataProtector protector = provider.CreateProtector("Test123");
string protectedPayload = protector.Protect("Hello world");
Console.WriteLine($"Protect returned: {protectedPayload}");
}
}
However, we are not using this functionality aynwhere in our application. Is it safe to remove AddDataProtection from ConfigureServices? Does any part of .NET Core application (TempData, AntiForgery tokens, ...) use it behind the scenes (so that Visual Studio doesn't find string IDataProtectionProvider)?
According to this document, here's a sentence said:
It cannot directly be used to protect or unprotect data. Instead, the
consumer must get a reference to an IDataProtector by calling
IDataProtectionProvider.CreateProtector(purpose)
hence based on this saying, it seems that data protection doesn't work as _rootProvider didn't be called in some place. And your another misgiving is some default setting or effect may work in some other places, you may refer to this document to see the common usage of data protection api.
And in my opinion, it's really hard to say it have no influence in your project as if by any chance we ignore some thing, that may lead to something unexpected. So if your app runs well now, why not just leave it there.
I've searched everywhere and nowhere does it mention where this key is defined, or similar ones such as HTTP_X_FORWARDED_FOR, REMOTE_ADDR etc.
MSDN documentation doesn't mention anything useful. The only thing close to useful I came about was some stackoverflow questions (this and this), along with this short blog post.
None of these sadly address my current question - from where does all these dictionary keys come from? Where is their specification, so that one knows they exist and learn to utilize them, by seeing the contents they hold?
EDIT: I'm using .NET Framework 4.6.0, where System.Net.Http's version is 4.0.0.0.
To get the client IP, I'm doing the following:
public string GetSomeIp(HttpRequestMessage request)
{
HttpContextWrapper context =
request.Properties["MS_HttpContext"] as HttpContextWrapper;
return (context == null) ?
string.Empty : (context.Request.ServerVariables["HTTP_X_FORWARDED_FOR"]
?? context.Request.ServerVariables["REMOTE_ADDR"]).Split(',')[0].Trim();
}
I'd like to find the documentation, which explains what MS_HttpContext does/holds in detail, as well as REMOTE_ADDR, HTTP_X_FORWADED_FOR, and where they are defined, so I can see all the other keys and more in detail of their implementation/proper usage.
I'm aware of the following server variables, but the keys used here are not mentioned there. (except REMOTE_ADDR)
I'd like to find the documentation, which explains what MS_HttpContext does/holds in detail
The System.Web.Http.* assemblies seem not described on the https://referencesource.microsoft.com/.
However, all these projects are now hosted on the GitHub as Open? Source https://github.com/aspnet/AspNetWebStack.
So far, I assume that this constant/key is used within a routine that assigns the System.Web -> HttpContext to System.Web.Http -> Request.
https://github.com/aspnet/AspNetWebStack/blob/master/src/System.Web.Http.WebHost/HttpRequestMessageExtensions.cs#L11-L44
Another occurrences are related to CORS and Tests. You can clone this repo and search for the "MS_HttpContext" occurrences in depth in you are looking for details. However, I am not sure about its documentation.
where this key is defined, or similar ones such as HTTP_X_FORWARDED_FOR, REMOTE_ADDR etc.
from where does all these dictionary keys come from?
These request's properties (aka Server Variables) are created (almost duplicated) from the corresponding headers sent by a client here (applicable for HttpContext - ASP.NET WebForms/MVC):
https://referencesource.microsoft.com/#System.Web/HttpRequest.cs,dc76880a3cfd0009
https://referencesource.microsoft.com/#System.Web/HttpRequest.cs,639
By the way, there are no more such properties in the HttpRequest for ASP.NET Core (headers only).
If you need to grab the client IP information, use any approach from the aforementioned (yours or suggested) links.
If you need to retrieve the familiar HttpContext instance (for example, outside the ApiController, for example, DelegatingHandler/aka middleware), use any of the approaches illustrated in this (already mentioned as well).
If you need to retrieve it inside the ApiController, it may be even easier (don't forget about required null ref checks and self-hosted WebAPI solutions):
public class YourApiController : ApiController {
public HttpResponseMessage YourActionName() {
var request = new HttpContextWrapper(CurrentContext).Request;
...
}
}
public class YourAuditHandler : DelegatingHandler {
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) {
string ipAddress = HttpContext.Current != null ? HttpContext.Current.Request.UserHostAddress : "0.0.0.0";
...
}
}
Context:
I am using DI in my Web application. (I am using NInject, but hopefully this should not matter)
Some places constructor injection is not possible, for example in my custom log4net database logger (that's not me, who instantiates my custom logger instead the log4net framework). So I am using my DI container there in service locator DP mode, and asking an instance resolve explicitly in the logger code.
Note this is just a sample, in many other cases I had to use NInject as service locator DP instead of constructor injection.
Now the problem:
I have an IAuditContextProvider which serves current request's audit data, like IP etc. The question arises how I configure my DI container to instantiate a concrete provider. So far I've used a request scope (singleton by request) what is supported out of box by NInject.
However recently I faced the fact I had to start a background processing initiated by a request. This is done by
// This is 'outside' it's actually a request serving method running in the request context, btw it is an MVC action method,
// Pseudo code:
var auditProvider = Locator.Resolve<IAuditProvider>()
Task.Run(() =>
{
// I would like to get the very same resolved auditProvider instance here as outside.
// Please note: outer local variables are not solution, because of implicit calls here inside, for example if there is a logging statement here, then the service locator in the custom logger must resolve the very same instance as outside
// Some how I must 'stamp' this thread to be the 'same' as the outside
// request thread in point of view of my custom scope resolver (see below)
}
Note: Configuring the DI container a wide scoped singleton are not solution because of multiple requests are server parallel, and they can not use a common auditProvider.
OK, I thought this is what for custom (resolving) scopes are for. Here is the pseudo code how I am configuring my DI container:
kernel
.Bind(typeof(IAuditContextProvider))
.To(typeof(WebAuditContextProvider)).InScope(dummy =>
{
// Here I have to return a very same object/number/id when in
// 'outside' the request thread, and inside the worker thread.
// This way I hopefully get the very same instance when resolving.
// To be short: I have no idea how?
});
I don't think there is a good answer for your question within the current bounds.
I do have an alternative suggestion - just perform the work synchronously in another process. This would require a form of inter-process communication (IPC) but shouldn't be too difficult.
A simple but effective form of IPC is just writing a record to a database table (acting like a queue) and having a windows service/daemon polling for new records to "process". In this example, you would put a record in the table with the contextual information (user id, etc) and the service would utilize this context to perform the work synchronously, but the workflow would be asynchronous to the Web UI.
This also has a nice side benefit: You can start to build monitoring, retry logic, etc into the service. These things are much harder to do reliably within an ASP.NET model.
You could forgo the database queue completely by using something like message queues/buses/events, but the basic concept is the same.
Update:
Did you try to use closures in C#? Like this:
var auditProvider = Locator.Resolve<IAuditProvider>()
Task.Run(() =>
{
// with closure you'll get that very variable you need:
auditProvider.SomeMethod();
}
You should read whole article about closures by John Skeet and how they can help you together with TPL.
Other useful information:
Such DI is being called as Ambient Context in famous book Dependency Injection by M. Seeman:
A truly universal CROSS-CUTTING CONCERN can potentially pollute a large part of the API for an application if you have to pass an instance around to every collaborator. An alternative is to define a context that’s available to anyone who needs it and that can be ignored by everyone else.
The AMBIENT CONTEXT is available to any consumer via a static property
or method. A consuming class might use it like this:
public string GetMessage() { return SomeContext.Current.SomeValue; }
In this case, the context has a static Current property that a consumer can access. This property may be truly static, or may be associated with the currently executing thread. To be useful in DI scenarios, the context itself must be an ABSTRACTION and it must be possible to modify the context from the outside—in the previous example, this means that the Current property must be writable. The context itself might be implemented as shown in the following listing.
The context is an abstract class, which allows us to replace one context with another implementation at runtime.
public abstract class SomeContext
{
public static SomeContext Current
{
get
{
// Get current context from TLS
var ctx = Thread.GetData(Thread.GetNamedDataSlot("SomeContext")) as SomeContext;
if (ctx == null)
{
ctx = SomeContext.Default;
Thread.SetData(Thread.GetNamedDataSlot("SomeContext"), ctx);
}
return ctx;
}
set
{
Thread.SetData(Thread.GetNamedDataSlot("SomeContext"), value);
}
}
public static SomeContext Default = new DefaultContext();
public abstract string SomeValue { get; }
}
TLS here stands for Thread Local Storage, which can be useful idea for you here.
Also I suggest you to read about OperationContext class, which can be helpful for you if you want to pass some context for your Task, something like this:
// save current context before task start
var operationContext = OperationContext.Current;
Task.Run(() =>
{
// set current operation context inside your Task with closure
OperationContext.Current = operationContext;
// Your work here
}
I'm working on a ASP.NET Web Api project and made it accept version information in the url.
For example:
api/v1/MyController api/v2/MyController
Now I would like to get the request version v1, v2 inside a custom LayoutRenderer for Nlog. Normally I would do this like the below example.
[LayoutRenderer("Version")]
public class VersionLayoutRenderer : LayoutRenderer
{
protected override void Append(System.Text.StringBuilder builder, NLog.LogEventInfo logEvent)
{
var version = HttpContext.Current.Request.RequestContext.RouteData.Values["Version"];
builder.Append(version);
}
}
The problem: HttpContext.Current is NULL
I believe this is because I use Async wrappers for NLog and some calls before the Logger are also Async.
A example of the logger being called Async inside Ninject.Extensions.WebApi.UsageLogger. At this point the HttpRequestMessage has all info we need to get the Version.
/// <summary>
/// Initializes a new instance of the <see cref="UsageHandler" /> class.
/// </summary>
public UsageHandler()
{
var kernel = new StandardKernel();
var logfactory = kernel.Get<ILoggerFactory>();
this.Log = logfactory.GetCurrentClassLogger();
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var startTime = DateTime.Now;
// Log request
await request.Content.ReadAsStringAsync().ContinueWith(c =>
{
this.Log.Info("{0}: {1} called from {2}", request.Method, HttpUtility.UrlDecode(request.RequestUri.AbsoluteUri), ((HttpContextBase)request.Properties["MS_HttpContext"]).Request.UserHostAddress);
this.Log.Info("Content-Type: {0}, Content-Length: {1}", request.Content.Headers.ContentType != null ? request.Content.Headers.ContentType.MediaType : string.Empty, request.Content.Headers.ContentLength);
this.Log.Info("Accept-Encoding: {0}, Accept-Charset: {1}, Accept-Language: {2}", request.Headers.AcceptEncoding, request.Headers.AcceptCharset, request.Headers.AcceptLanguage);
if (!string.IsNullOrEmpty(c.Result))
{
if (this.MaxContentLength > 0 && c.Result.Length > this.MaxContentLength)
{
this.Log.Info("Data: {0}", HttpUtility.UrlDecode(c.Result).Substring(0, this.MaxContentLength - 1));
}
else
{
this.Log.Info("Data: {0}", HttpUtility.UrlDecode(c.Result));
}
}
});
var response = await base.SendAsync(request, cancellationToken);
// Log the error if it returned an error
if (!response.IsSuccessStatusCode)
{
this.Log.Error(response.Content.ReadAsStringAsync().Result);
}
// Log performance
this.Log.Info("Request processing time: " + DateTime.Now.Subtract(startTime).TotalSeconds + "s");
return response;
}
The question
What would be the best way to make the VersionLayoutRenderer work in a generic way? Could I add a MessageHandler and Bind the HttpRequest to some Async scope? If so any guidelines would be much appreciated cause I'm still getting used to Ninject.
For the time being I add the version information directly to the Log Call in the UsageHandler, but I would really like a more generic solution, where I can always rely on version information inside my logging.
Edit: Updated the question to be more specific and included more details.
Try injecting the context using something like:
kernel.Bind<IDependency>()
.To<Mydependency>()
.InRequestScope()
.WithConstructorArgument("context",c=>HttpContext.Current);
The actual issue is really neutral wrt what you should do with Ninject - you just need to get the phasing of your processing such that any objects that are going be running async have everything they need without relying on the magic HttpContext.Current. Get that working with no DI Container first.
Then, to use Ninject the major steps are:-
Your Bind statements need to be run once. See the Ninject.MV3 wiki for the best approach (until it gets merged in, there is not OOTB with the NuGet-based edition)
as #rickythefox (+1'd) says, your registration should bake the thread/context-relative data into the object and you config the registration such that it can happen early in request processing, when you're still on the thread that's HttpContext.Current
kernel.Bind<ILogger>()
// TODO replace GCCL with something like GetClassLogger(ctx.Request.Service.ReflectedType) - see the wiki for examples
.ToMethod( ctx=> ctx.Get<ILoggerFactory>().GetCurrentClassLogger())
.InRequestScope()
.WithConstructorArgument("context",c=>HttpContext.Current);
Then just make the constructor of the handler take a ILogger, which can be assigned to .Log (which I hope isnt static :D)
NB, the aim is for you never to write a kernel.Get(), ever, period.
The real problem here though, is that proper use of WebApi does not involve using HttpContext.Current or any other magic static methods or anything similar (for testability, to make yourself independent of the hosting context (self hosting, OWIN etc), and many more reasons).
Also, if you are using NLog (or Log4Net) you should also look at the Ninject.Extensions.Logging package (and source).
The GlobalConfiguration class can provide you access to the routing configuration.
// The code below assumes a map routing convention of api/{version}/{controller}/....
// This will give you the configured routes
var routes = GlobalConfiguration.Configuration.Routes;
// This will give you the route templates
var templates = routes
.Select(route => route.RouteTemplate);
// This will give you the distinct versions for all controllers
var versions = routes
.Select(route => route.RouteTemplate)
.Select(template => template.Split("/".ToCharArray(), StringSplitOptions.RemoveEmptyEntries))
.Select(values => values[1])
.Distinct();
// This will give you the distinct versions for a controller with the specified name
var name = "MyController";
var controllerVersions = routes
.Select(route => route.RouteTemplate)
.Select(template => template.Split("/".ToCharArray(), StringSplitOptions.RemoveEmptyEntries))
.Where(values => String.Equals(values[2], name, StringComparison.OrdinalIgnoreCase))
.Select(values => values[1])
.Distinct();
I am not sure if you are trying to resolve the version with a known value (the name of the controller) or if you are trying to dynamically resolve it. If you inject the current HttpContext, you can use the request URI of the context to filter the routes via the route template.
Edit: After your comments I realized the routing configuration isn't what you were after.
If the end goal is to implement logging within your controllers, you may want to take a look at Tracing in ASP.NET Web API as there is support for tracing built-in to the Web API infrastructure.
I have a logging class that, well, logs things. I would like to add the ability to automatically have the current page be logged with the messages.
Is there a way to get the information I'm looking for?
Thanks,
From your class you can use the HttpContext.Current property (in System.Web.dll). From there, you can create a chain of properties:
Request
Url and RawUrl
The underlying object is a Page object, so if you cast it to that, then use any object you would normally use from within a Page object, such as the Request property.
It's brittle and hard to test but you can use System.Web.HttpContext.Current which will give you a Request property which in turn has the RawUrl property.
public static class MyClass
{
public static string GetURL()
{
HttpRequest request = HttpContext.Current.Request;
string url = request.Url.ToString();
return url;
}
}
I tried to break it down a little :)
In the past I've also rolled my own logging classes and used Console.Writeln() but really there are a number of good logging options that already exist so why go there? I use NLog pretty much everywhere; it is extremely flexible with various log output destinations including console and file, lots of log format options, and is trivial to set up with versions targeting the various .net frameworks including compact. Running the installer will add NLog config file options to the Visual Studio Add New Item dialog. Using in your code is simple:
// declare in your class
private static Logger logger = LogManager.GetCurrentClassLogger();
...
// use in your code
logger.Debug(() => string.Format("Url: {0}", HttpContext.Current.Request.Url));