Write to a log file in ASP.NET - asp.net

I am writing event data to a log file in an asp.net httphandler by using the File.AppendAllText method. I am concerned with what will happen when multiple requests are received simultaneously. Does AppendAllText lock the file it's writing to?

I recommend using the TextWriterTraceListener instead of trying to manage this your self.
It is very simple to setup and use:
TextWriterTraceListener logListener = new TextWriterTraceListener("C:\log.txt", "My Log Name");
Trace.Listeners.Add(logListener);
And then to log something:
Trace.WriteLine("Log this text");
It is very simple to use and also there are many different types of listeners for SQL, Event Log, text file, etc. So you won't have to adjust your code if you want to change out the listener.

no, you should have a static lock object guarding the log-file write, e.g.
public static object LockingTarget = new object();
public void LogToFile(string msg)
{
lock(LockingTarget)
{
//append to file here as fast as possible
}
}

You can use My.Log to write to log files.
Edit: If you use the FileLogTraceListener, that listener is thread-safe.
Doing this also allows you to control and configure the logging through the web.config file.

Related

in ASP.NET core (.NET5) how can i write logs for each request on separate files? Using Serilog or other

I'm new to .NET and to webservice development, so i'm not exactly sure how to implement the requirement i have.
My webservice gets a POST request with some data, which i need to
process to generate a pdf file: name_YYYYMMDDHHmmss.pdf.
For monitoring this i want to have a separate logfile for each request, named like the output file: name_YYYYMMDDHHmmss.log
I would like to avoid passing a config object into every class/function in which i need to add stuff to the log file
I've managed to install Serilog and it works for what i need, but not when i get concurrent requests. I'm also not exactly sure how simultaneous requests are handled in .NET (i have no thread specific code written so far), but as far as i can tell, when i change Global Logger file name, that object is shared across all threads so all of them write to the same file.
I've looked at a bunch of solutions, but i haven't managed to find nothing that suits this, and it seems most people have everything into 1 file...
Is there any clue or tips you can give me? I'm open to using something other than Serilog.
One way to have dynamic file names based on a specific context is by using the Serilog.Sinks.Map and then, via a middleware in the request pipeline, you can add a property to the log context that drives the file name to be used when writing to the log.
Examples of similar usage of Serilog.Sinks.Map to decide which file name to use at run-time:
Serilog - can not log to multiple files based on property
In Serilog dynamically changing log file path?
The best solution that I found to this problem was using Serilog.Sinks.Map. I configured my Logger something like this:
Log.Logger = new LoggerConfiguration()
.WriteTo.Map("Name", "Default", (name, wt) => {
var fileName = name == "Default" ? "log" : $"{log-{name}}"
wt.File($"./{fileName}-.txt");
}).CreateLogger();
Then on my controller, on each method where I needed this feature, I enclosed all the instructions inside a LongContext like this:
[HttpGet]
public IHttpActionResult Get() {
using (LogContext.PushProperty("Name", "theFileName") {
// ...
_myService.Method1();
// ...
}
}
public class MyService : IMyService {
// ...
public void Method1() {
// ...
Log.Information("This is what happened at this point…");
// ...
}
// ...
}
So all the Log's inside will use that context and it will write on a different file with the name you set for that context without having to modify any Log.Information/Error/Warning/etc that you already have on your code.
This is the ugly part... you have to define a context on a root place in order to make those Logs write on a different file. So for a controller method, the first thing you have to do is to enclose all with a LogContext.

Folder browsing in ASP.net

What I'm trying to do here is to allow my user to select a path in a data server on a network, so that I could generate a configuration file.
I hope to be able to replicate the function of OpenFileDialog() on my asp.net page. However this function does not exist on asp.net, and I do know that there is this control in asp.net call FileUpload. But what I required here, is just the path/directory for the folder. I do not require my files to be uploaded.
How can it be done?
Doing this in a web application is tricky. You would have to enumerate the folders on the server that you want to browse (presumably this is the same server that's running the web application), and then present that hierarchy to the user to select a folder. If it's not too big a hierarchy, you could just enumerate the whole bunch up front, and display it in a tree. If it's big for that, you could use an Ajax approach: select the top-level folder, then send an Ajax request to get the next level, and so on.
To enumerate the folders, you'll need to walk the filesystem yourself. See http://msdn.microsoft.com/en-us/library/dd997370(v=vs.100).aspx for one way.
No, there is no inbuilt control for this. It is not a normal requirement cause most site don't let their users see their file structures.
Building a user control that does this will be simple though.
I suggest using a TreeView asp.net control, attached to your datasource where you have listed the files.
This sample on binding a treeview should get you started.
You can populate your data using
var path = Server.MapPath("/");
var dirs = Directory.[EnumerateDirectories][2](path);
var files = Directory.[EnumerateFiles][3](path );
Finally to make it look like a dialog, you could use the jQuery UI dialog component.
The solution I have found is, this is just for anyone looking for answer:-
protected void browse_Click(object sender, EventArgs e)
{
Thread thdSyncRead = new Thread(new ThreadStart(openfolder));
thdSyncRead.SetApartmentState(ApartmentState.STA);
thdSyncRead.Start();
}
public void openfolder()
{
FolderBrowserDialog fbd = new FolderBrowserDialog();
DialogResult result = fbd.ShowDialog();
string selectedfolder = fbd.SelectedPath;
string[] files = Directory.GetFiles(fbd.SelectedPath);
System.Windows.Forms.MessageBox.Show("Files found: " + files.Length.ToString(), "Message");
}
The asp.net site is a completely disconnected environment to your server. As other people have mentioned, to replicate an OpenFileDialog() you will need to look at the folder structure and present this to the user in the web/disconnected environment. In this case the user is abstracted from the actual file system... since this abstraction already occurs, it would be a good time to consider the route you're taking. It might be worth considering that a direct replication of the file system is not required, you could manage the "virtual" folder structure in the database with links/paths to files on disk are maintained there?

How to dynamically load and switch the resource file in the web app (ASP.NET) without recompiling?

I would like to store the resource files (containing texts for labels etc.) for my web application in the database to be able to edit and create them dynamically later (probably in the UI). My idea was to store the whole resx file in an xml column and simply load it on demand - depending on the language and some other attributes of the current user when he is logging into the application or switching the context. The important thing is that the resources do not only depend on the culture info of the user but also on some context information that can be switched by user without logging off and on again (in our case called "group", not having anything to do with a group of users).
Is it possible somehow to load the contents of the resources from an external source and simply switch them without web application being recompiled by the server ? I know that there are some interfaces on which I could hook up and implement a custom resources provider which reads from the database directly but if it could work somehow with the resx files it would probably make things a lot easier..
Pretty late but since there is no answer as of yet.
System.Resources.ResourceReader resourceReader
= new System.Resources.ResourceReader("PathToResourceFile");
That's pretty much it. Now you can create resource files like en.resx or de.resx and load them depending on the users language. Something like
System.Resources.ResourceReader resourceReader
= new System.Resources.ResourceReader(HttpContext.Current.Request.UserLanguages[0]
+ ".resource");
Keep in mind to provide a default language (resource file) for a user with a language you don't support.
Edit:
Take a look at this link.
The question is 6 years old, but I'm still gonna answer it :)
To read .resx files, you need to use System.Resources.ResXResourceReader class from System.Windows.Forms.dll
This is nicely explained here. Just a quick sample for completeness:
using (ResXResourceReader resxReader = new ResXResourceReader(#".\CarResources.resx"))
{
foreach (DictionaryEntry entry in resxReader) {
// ...
}
}
Sure you can do this easily, and it works for straight XML files. You don't need to use a resx file.
/// <summary>
/// Sets or replaces the ResourceDictionary by dynamically loading
/// a Localization ResourceDictionary from the file path passed in.
/// </summary>
/// <param name="resourceDictionaryFile">The resource dictionary to use to set/replace
/// the ResourceDictionary.</param>
private void SetCultureResourceDictionary(String resourceDictionaryFile)
{
// Scan all resource dictionaries and remove, if it is string resource distionary
for ( int index= 0; index < Resources.MergedDictionaries.Count; ++index)
{
// Look for an indicator in the resource file that indicates the resource is
// swappable. For instance in our files the header contains this:
// <sys:String x:Key="ResourceDictionaryName">Resources-en-CA</sys:String>
if (Resources.MergedDictionaries[index].Contains("ResourceDictionaryName"))
{
if ( File.Exists(resourceDictionaryFile) )
{
Resources.MergedDictionaries.Remove(Resources.MergedDictionaries[index]);
}
}
}
// read required resource file to resource dictionary and add to MergedDictionaries collection
ResourceDictionary newResourceDictionary = new ResourceDictionary();
newResourceDictionary.Source = new Uri(resourceDictionaryFile);
Resources.MergedDictionaries.Add(newResourceDictionary);
}

Reading Application Configuration during Application_Start

Consider a web application that resizes large tiff files on the fly. Each large tiff file is resized into a jpg thumbnail and larger jpg when the user invokes the operation. The dimensions of these converted files is always the same.
During a code review yesterday, one of the other developers asked me why I set those dimensions in my global.asax like so:
Application["resizedImageWidth"] = int.Parse(ConfigurationManager.AppSettings["ResizedImageWidth"]);
, instead of just looking up the value via a Configuration file during the actual method invocation. I store the widths in the configuration file in the event the end user's after testing the application would like to alter dimensions so I would not have to change code inline.
The reasoning I gave was to prevent the read from the configuration file each time an image was generated, but could not answer if there was similar overhead during a lookup to application level variables. This optimization probably doesn't affect performance to a large scale, but I wanted to know what the community thought the more efficient solution was, i.e. set them during Application start up, or read them on the fly during method invocation.
Generally you should read from the configuration on the fly as you need it. The framework will cache the configuration file, so it is fairly performant. And I believe (Don't quote me) that ASP.Net can monitor and bring in the changes to a configuration file without restarting the application.
I typically like to create a Configuration class which will hide the details of where the value is stored:
public static class Config
{
public static int GetXDimension()
{
...
}
}
This keeps your calling code clean from the configuration code, and if you find I'm wrong, you won't have to change your code everywhere.
You are correct to store this value in an Application object. Indeed, that is the express purpose behind the provision of Application: to hold values that are to be available across all pages and users of the application.
It will indeed be more performant as the application settings are held in memory. It should be pretty obvious that reading from memory is significantly faster than reading from your configuration file (not to mention the lookup via the ConfigurationManager.AppSettings class method and the integer parse).
Any changes to the web.config file will indeed result in a restart of the application. Indeed, this is how I restart my application (when needed) during testing. If you need to change this value "on the fly" you may even want to store this value in a "Configuration" table in your database rather than keeping it in the Web.config file so that changing it doesn't reboot the App.
One trick that I have used is similar to Josh's Config class: I have a "BusinessLogic" class and I place a small handful of settings in static variables of this class so that I can access them in a type-safe manner. It isn't hard and the payoff is pretty obvious in terms of readability.
I would recommend expanding Josh's answer and call it on demand, but then cache it into a private variable:
public static class Config
{
private static int? xDimension;
public static int GetXDimension()
{
if(xDimension == null)
xDimension = int.Parse(ConfigurationManager.AppSettings["ResizedImageWidth"]);
return xDimension.Value;
}
}
or into the Application:
public static class Config
{
public static int GetXDimension()
{
if(Application["xDimension"] == null)
Application["xDimension"] = int.Parse(ConfigurationManager.AppSettings["ResizedImageWidth"]);
return (int)Application["xDimension"];
}
}

ASP.NET Localized web site -- updating on the fly

I think I have a solution to this, but is there a better way, or is this going to break on me?
I am constructing a localized web site using global/local resx files. It is a requirement that non-technical users can edit the strings and add new languages through the web app.
This seems easy enough -- I have a form to display strings and the changes are saved with code like this snippet:
string filename = MapPath("App_GlobalResources/strings.hu.resx");
XmlDocument xDoc = new XmlDocument();
XmlNode xNode;
xDoc.Load(filename);
xNode = xDoc.SelectSingleNode("//root/data[#name='PageTitle']/value");
xNode.InnerText = txtNewTitle.Text;
xDoc.Save(filename);
Is this going to cause problems on a busy site? If it causes a momentary delay for recompilation, that's no big deal. And realistically, this form won't see constant, heavy use. What does the community think?
I've used a similar method before for a very basic "CMS". The site wasn't massively used but it didn't cause me any problems.
I don't think changing a resx will cause a recycle.
We did something similar, but used a database to store the user modified values. We then provided a fallback mechanism to serve the overridden value of a localized key.
That said, I think your method should work fine.
Have you considered creating a Resource object? You would need to wrap your settings into a single object that all the client code would use. Something like:
public class GuiResources
{
public string PageTitle
{
get return _pageTitle;
}
// Fired once when the class is first created.
void LoadConfiguration()
{
// Load settings from config section
_pageTitle = // Value from config
}
}
You could make it a singleton or a provider, that way the object is loaded only one time. Also you could make it smart to look at the current thread to get the culture info so you know what language to return.
Then in your web.config file you can create a custom section and set restartOnExternalChanges="true". That way, your app will get the changed when they are made.

Resources