Once one has a logging and tracing setup using log4net in place for ASP.NET Web API, what are the specific aspects that need to be logged and/or traced?
I am asking this specifically from Web API perspective. Is there a series of MUST Log this or MUST trace this.
Like, INFO traces about a controller's request, any NULL checks, etc.
Is there a reference list that can be validated against to ensure optimum logging and tracing coverage in ASP.NET Web API ?
So I will assume your goal for the logging is, as it should be, to debug the application rather than measure for performance.
While I think there is no one 'right' answer to this, at the minimum I would always try to log
Timestamps, class/function names and thread name (if yr running a multithreaded app) in every log line
Quick log line # every entry point, along with the full contents of
the passed in request, preferably in a format that would make it
easier for you to reissue the same request from fiddler or a similar
tool (this can be done via a custom action filter - see how the
author uses these for performance monitoring a web api app here )
If you are doing any database queries as part of your action, log
the full query sql string if possible (once again to make it easier
to repeat during debugging) as well as at least the # of rows
returned
If you are doing any external service calls, log both the full
request and response strings
Use try/catch blocks to log any exceptions (you can also use
something like the ELMAH library to do this automatically rather
than handling in code - link here and here
Anything that is resolved during runtime - think values from config files, database queries, calculated values, should also be logged
I'm sure there is more missing from this list - after all this varies on what yr doing in the app. You should be asking yourself at every step of the way - what could go wrong in my application? What things would I or the poor chap that will work on this after me find useful whilst debugging a problem?
If you are hosting your webapi in IIS, you can enable IIS LOG and counters:
http://www.iis.net/configreference/system.applicationhost/sites/site/logfile
Performance Counters:
http://msdn.microsoft.com/en-us/library/fxk122b4%28v=VS.100%29.aspx
http://www.iis.net/learn/extensions/configuring-application-request-routing-(arr)/using-performance-counters
An additional category of items to log for a service code that implements authorization authentication and impersonation where elevated access is required. These should be configurable to log either success failure or both.
Related
In the new .NET 7.0 framework, things have changed considerably...
This has been asked a gazillion times before and if the .NET Core framework would not break all kinds of backwards compatibility then I would have an easy answer. Too bad a lot of answers are related to .NET 4.9 and older and they just don't work.
So in my minimal Web API I want to do some simple request logging by sending the complete URL as a string to a database. (With some additional information.) So I have the HttpContext class (NOT the HttpContext class) with the Request property of type HttpRequest and it just does not have any method to get the original URL that was requested. Only the various parts which I have to concatenate and hope it resembles the original uri...
So, a useful method like Request.Url is now totally gone and the DisplayUrl helper() isn't providing me everything. It leaves out the QueryString. I need that QueryString also.
And yes, I can concatenate this again to get the value that it originally received and made hidden. It just feels wrong, though. Plus, this method makes the uri suitable to be returned in a header. Again, I want to store it in a database for logging purposes exactly as it originally was!
So now I have to ask something that has been asked a gazillion times before, simply because the latest .NET update breaks things again.
The application I'm working on is a multi-tenant application running on multiple domain names including wildcard subdomains and is used to analyze the amount of traffic I get for new domains that I've registered. The whole API will just generate 404-errorcodes back to the user, but I want the whole URL to get registered to determine if the domain isn't getting any funny requests. (Like hackers trying to access https://owa.example.com/wp-booking.php or https://forum.example.com/default.aspx?g=rsstopic&pg=0&ft=0 or whatever.) I also log the body of the request, the request method, the IP address of the user and the headers that are passed and it cal be used by me to extend a blacklist of users who seem to have malicious intent.
The domains where I use it are often fresh out of quarantine and are just in a wait-state until development starts. (Or until someone takes it over.) Responding with a 404-error should tell users (and hackers) that the site does not exist any more. Most users will be aware that the site is gone so they stop visiting, but various automated (and hacking) tools might still be running so the information tells me what the user is trying to do.
Anyways, I need the full URL with the query string, domain name, protocol and everything else that the client has passed to my server. But .NET 7 is preventing me from access to the original URL which is dumb. And the whole project is basically a single app.Run() statement which always returns a 404 error after logging the request. And yes, slow is fine for this API.
Sigh... Request.Url.AbsoluteUri was such a useful function, but it's gone, making all answers going back 14 years or so obsolete as Request has no Url...
UriHelper.GetDisplayUrl(source) should do the trick:
app.MapGet("test_uri", (HttpContext context) => context.Request.GetDisplayUrl());
Returns http://localhost:5207/test_uri?query=1&test=2 for this url.
So in my minimal Web API I want to do some simple request logging by sending the complete URL as a string to a database.
Minimal APIs support request logging - docs, example. Potentially you can combine it with some logging library which allows writing to database.
I have some kind of a job scheduling implemented which calls a function ProcessJob. Now inside this method I need to generate url to one of my pages i.e DoanloadPage.aspx?some_params. That url is sent to user via email and when user clicks that link, it will take to the page.
The problem here is that I am not generating url in a web request method or I don't have access to the Request object. URL need to be generated in a custom class which is threaded i.e not in a web request.
So I can't go with these solutions:
HostingEnvironment.MapPath("test.aspx");
VirtualPathUtility.ToAbsolute("123.aspx");
HttpContext.Current.Request.Url.Authority;
None of these works because I think they all rely on current request or session somehow. So how do I generate urls for my app inside my code so I can use them anyway I want.
If your method cannot use HttpContext.Current.Request.Url, for example in case it's a background scheduled task, then you can use either of the following options:
In case that your code is hosted in the same ASP.NET application, you can pass the site domain name of the site to your class, in the first request. To do so, you need to handle Application_BeginRequest event and get the domain from HttpContext.Current.Request.Url and then pass it to your class, or store it in an application scope storage. You can find an implementation in this post or the original article.
Note: The code is available in SO, so I don't repeat the code
here.
If your code is not hosted in the same ASP.NET application or if for any reason you don't want to rely on Application_BeginRequest, as another option you can store the site domain name in a setting (like appsettigs in app.condig or web.config if it's web app) and use it in your code.
You can do something like this. Dns.GetHostName will return the name of the computer that is hosting the site. You can use that to check if the site is on a development server.
string domain = "www.productionurl/123.aspx";
if (Dns.GetHostName() == "Development")
{
domain = "www.developmenturl/123.aspx";
}
The Dns.GetHostName() is not the only way to check. You could also use the HostingEnvironment.ApplicationPhysicalPath. You can check that also and see if the path is that of the development server.
My answer is: don't do this. You're building a distributed system, albeit a simple one, and generally speaking it is problematic to introduce coupling between services in a distributed system. So even though it is possible to seed your domain using Application_BeginRequest, you are then tying the behavior of your batch job to your web site. With this arrangement you risk propagating errors and you make deployment of your system more complicated.
A better way to look at this problem is to realize that the core desire is to synchronize the binding of your production site with the URL that is used in your batch job. In many cases an entry in the app.config of your batch would be the best solution, there really isn't any need to introduce code unless you know that your URL will be changing frequently or you will need to scale to many different arbitrary URLs. If you have a need to support changing the URL programmatically, I recommend you look at setting up a distributed configuration system like Consul and read the current URLs from your deployment system for both the IIS binding and the app.config file for your batch. So even in this advanced scenario, there's no direct interaction between your batch and your web site.
I have a .NET a web app that i built for files processing .I am using IIS 7 anonymous user authentication , i also did not require the users to log in, so pretty much any user who has access to the intranet can access the web app.
The users said when two of them try to run their files on app at the same time they receive an error( did not specify it).
My question is :
If i use anonymous authentication is it by default every user will have his\her own session while accessing the app?
Yes, by default every user will have their own session. And anonymous authentication is the default scheme for the web. It is unlikely that any web server, by default, would only allow 1 anonymous user at a time.
Most likely, if your app is doing file processing, you may be dealing with file locks and not an issue with IIS. You want to make sure that your code is written so that, if two or more people access it simultaneously, they can not request to same file. Also, you need to make sure that you are properly closing any file streams you open, even in the case of exceptions. Without seeing the code in question, it would be difficult to impossible to give more specific guidance, but hopefully this will help point you in the correct direction.
Install Elmah to get error report of ypur app!
In my c# web application, I need to intercept all the incoming requests(get) to test if the URL contains specific file name (not the file type or extension) or string, based on this information I need to check User's authentication and authorization status and resource's permission status and redirect user to login if required. My question is what is the best place to test this? Global.asax, http handler, http module or ?
p.s. This is a busy website and I need to consider the performance as well. The secure resources are present in the same directory containing the public documents but only database has the access information about public/private.
thank you
As you want to inspect all requests and are not limited to a specific file extension, I'd propose to create a HttpModule. This is also backed by MSDN:
Typical uses for HTTP modules include the following:
Security Because you can examine incoming requests, an HTTP module
can perform custom authentication or other security checks before the
requested page, XML Web service, or handler is called.
It is also a good choice in terms of performance as the module is run early in the request. If the user is not authorized, he or she is redirected early. In order to increase performance, you can also think about implementing some kind of caching so that you do not have to query the database on each request.
For a walkthrough on how to create a module, see this link.
Implementing the check in Global.asax is also an option, but this ties your solution to this application. You can easily share the module with another project. Using a module is also good from a design point of view as you create a specific module for this task and do not mix functionality as you maybe would in the BeginRequest method of the Global.asax sooner or later.
What is your best solution for pulling out an exception occurring on a user computer?
For example I send automatically a email when an exception is thrown which body contains the call stack.
Have you experience other quick way to handle user bug?
Unless this is a corporate app, be sure to ask the user if they want to submit anything first.
Then, generally I would recommend submitting it via web service. Better to leverage the end-user's existing proxy settings if any.
ELMAH is a good tool for logging exceptions in ASP.NET websites - it can log the exception to a local database as well as emailing it.
"Once ELMAH has been dropped into a
running web application and configured
appropriately, you get the following
facilities without changing a single
line of your code: Logging of nearly
all unhandled exceptions.
A web page to remotely view the entire log of recoded exceptions.
A web page to remotely view the full details of any one logged
exception.
In many cases, you can review the original yellow screen of death that
ASP.NET generated for a given
exception, even with customErrors
mode turned off.
An e-mail notification of each error at the time it occurs. An
RSS feed of the last 15 errors from
the log."
BugzScout with FogBugz worked wonders for me. An API to your bug database in general is the way to go.
With respect to emailing it, you can simulate some of the features of a bug tracking database but it's not as good as the real thing. If you keep with the email option, be sure to send them to a generic bugs#companyX.com email instead of a personal so they can be routed to a different person quickly if necessary.