log4net how do I get the output of Log errors? - c#

I have a c# web application and I would like to be able to see errors being thrown in the Service.asmx.cs section. I.e. when the sql statement fails or I spelled a variable name wrong.. etc. I'm fairly new to .net but the template for the app that I have has log4net installed.
protected static log4net.ILog Log = EarthSoft.Common.log4net.GetLogger(typeof (Service));
Then my get method is :
[WebMethod(Description = "Get a list of facilities")]
public List<Facility> GetFacilities()
{
try
{
var context = EarthSoft.Server.Helpers.RequestContext.GetRequestContext(true);
if (context.Connection == null || context.User == null) return null;
var lst = new List<Facility>();
string sql =
string.Format("select analyte_full,conc from dt_biological");
var cmd = context.Connection.CreateCommand(sql);
context.Connection.PrepareTextCommand(cmd);
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
lst.Add(new Facility()
{
name = (string)reader.GetValue(0),
distance = (decimal)reader.GetValue(1)
});
}
}
context.Connection.Close();
return lst;
}
catch (Exception ex)
{
Log.Error(ex.Message, ex);
return null;
}
}
So the error message is appended to the Log here but I have no idea how to
access it or where it writes the log? Is it possible the log output is saved onto a table into the sql database somewhere?

Log4Net is higly configurable, and to specify "where" to log, it uses the "appender" concepts. There is a lot of appender already done that write in files/database/queues and you can write your own, if needed ( normally not ).
Anyway start to look at the examples from here: https://logging.apache.org/log4net/release/config-examples.html
Somewher ein your code, typically in the application initialization, you have to call:
XmlConfigurator.Configure()
This will read the configuration from the application configuration file.
Alternatively you can configure log4net programmatically, but it can be sort of advanced if you never used before with manual configuration, so I suggest to start looking at the examples above and use configuration from file.
If no configuration is supplied, log4net work perfectly but... no log are produced at all, and I think this is the reason you can't find any log at the moment.

Related

Accessing file with streamreader failed because it is being used by another process

I have a .NET Core application which is multithreaded. One aspect of the application is a health check which parses a log file for errors. This is the code used to access it:
using StreamReader reader = new StreamReader(GetLogFile);
I noticed that I occasionally get this error:
2021-01-12 11:15:14.890Z ERROR APP=2227 COMP=3789 [16] Health check Check logs for application issues threw an unhandled exception after 96.2407ms - Logger=Microsoft.Extensions.Diagnostics.HealthChecks.DefaultHealthCheckService,Level=ERROR,ThreadId=16,,Exception="System.IO.IOException: The process cannot access the file 'c:\apps\Cb.Publisher\Logs\Cb.Publisher.log' because it is being used by another process.
I changed my code to this:
using StreamReader reader = new StreamReader(File.OpenRead(GetLogFile));
In testing it I haven't encountered the issue but it occurred so rarely that I am not 100% sure it's resolved. Is my change likely to resolve this issue or is there a better way to do it?
Additional Info
This is the entire function:
private int LogLine(Regex reg)
{
GetLogFile = DefaultLogFile.GetLogFileName();
using StreamReader reader = new StreamReader(File.OpenRead(GetLogFile));
string line;
int lineNo = 0;
int errorLine = 0;
while ((line = reader.ReadLine()) != null)
{
Match match = reg.Match(line);
if (match.Success)
{
errorLine = lineNumber;
}
lineNo++;
}
return errorLine;
}
If I set a breakpoint on the while line in Visual Studio and run the function, then try to edit the file in Notepad I fails with the error The process cannot access the file because it is being used by another process.
After some investigation I'm wondering if this line could actually be the cause of my problems:
var fileTarget = (FileTarget)LogManager.Configuration.FindTargetByName("file-target");
It's in DefaultLogFile.GetLogFileName:
public string GetLogFileName()
{
var fileTarget = (FileTarget)LogManager.Configuration.FindTargetByName("file-target");
var logEventInfo = new LogEventInfo();
string fileName = fileTarget.FileName.Render(logEventInfo);
if (!File.Exists(fileName))
{
throw new Exception("Log file does not exist.");
}
return fileName;
}
You currently suggested solution will likely be enough, yes:
using StreamReader reader = new StreamReader(File.OpenRead(GetLogFile));
However, the proper solution is for you to check that the file is not being locked from wherever else you are using the file. This also means your logging framework (be it Log4Net, NLog, Serilog etc.) should be properly configured to not take an exclusive lock on the log file. I believe logging frameworks usually do not lock it from read access by default, so unless you have customized the configuration the logging framework should not be a problem.

SharePoint ClientContext.ExecuteQuery works in c# application but crashes in DLL

I have written C# code to search for specific file types in SharePoint lists within a site and display the file names in a listview.
The code works perfectly well in a C# windows application, but when it is compiled into a C# DLL and called from a Delphi2007 application it crashes when it hits the first call to ClientContext.ExecuteQuery(). There is no exception or error message - the Delphi application just stops running.
The really weird part is that my Delphi test application has a web browser component, and if I use that to navigate to the top level list on the site the DLL then works OK.
The question therefore is why does the first ExecuteQuery call fail in the DLL if I haven't logged on to the site first?
This is the C# code:
public void ListFiles()
{
string LContains = "<Contains><FieldRef Name='FileLeafRef'/> <Value Type ='Text'>{0}</Value></Contains>";
string LNotEqual = "<Contains><FieldRef Name='FileLeafRef'/><Value Type ='Text'>{0}</Value></Contains>";
string LWhereQuery = "";
switch (comboFileType.SelectedIndex)
{
case 0: LWhereQuery = string.Format(LContains, ".DOC"); break;
case 1: LWhereQuery = string.Format(LContains, ".PDF"); break;
case 2: LWhereQuery = string.Format(LNotEqual, "xxxx"); break;
}
Uri LUri = new Uri(SharePointURL);
using (SP.ClientContext LContext = new SP.ClientContext(SharePointURL))
{
System.Net.CredentialCache cc = new System.Net.CredentialCache();
if (!string.IsNullOrEmpty(Domain))
cc.Add(LUri, AuthenticationType, new System.Net.NetworkCredential(UserName, Password, Domain));
else
cc.Add(LUri, AuthenticationType, new System.Net.NetworkCredential(UserName, Password));
LContext.Credentials = cc;
LContext.AuthenticationMode = SP.ClientAuthenticationMode.Default;
var LWeb = LContext.Web;
lvItems.BeginUpdate();
try
{
try
{
SP.List LList = LWeb.Lists.GetByTitle(DefaultListName);
SP.CamlQuery LQuery = new SP.CamlQuery();
LQuery.ViewXml = "<View Scope='RecursiveAll'><Query><Where>"
+ LWhereQuery
+ "</Where></Query><RowLimit> 30 </RowLimit></View>";
SP.ListItemCollection LItems = LList.GetItems(LQuery);
LContext.Load(LItems);
LContext.ExecuteQuery(); **<<<< Crash happens here**
foreach (SP.ListItem LItem in LItems)
{
SP.File LFile = LItem.File;
LContext.Load(LFile);
LContext.ExecuteQuery();
var LViewItem = new ListViewItem();
try { LViewItem.Text = LFile.Name; }
catch { LViewItem.Text = "!Error"; }
try { LViewItem.SubItems.Add(LFile.TimeLastModified.ToString()); }
catch { LViewItem.SubItems.Add("!Error"); }
if (LFile.CheckOutType != Microsoft.SharePoint.Client.CheckOutType.None)
{
try { LViewItem.SubItems.Add(LFile.CheckedOutByUser.LoginName); }
catch { LViewItem.SubItems.Add("!Error"); }
}
else
LViewItem.SubItems.Add("Not checked out.");
try { LViewItem.Tag = LFile.ServerRelativeUrl; }
catch { LViewItem.Tag = "!Error"; }
lvItems.Items.Add(LViewItem);
}
}
}
catch (Exception ex)
{
MessageBox.Show("Error: " + ex);
}
}
finally
{
lvItems.EndUpdate();
}
}
The code is in the .cs of a dialog form in the DLL. The form displays as it should and the crash only happens when I click a button to do the search.
I put some debug code in to check all the string params etc. (by writing them to a text file) and they are all OK.
I tried debugging the DLL from VS by specifying the D2007 app as the 'startup external program' but I can't get breakpoints to work and at the point where it crashes it says: An unhandled exception of type 'System.StackOverflowException' occurred in Microsoft.SharePoint.Client.Runtime.dll and suggests I might have an infinite recursive call but, as mentioned earlier, the code all works perfectly if I have already logged into the site and browsed to the top level list, so i don't think it is a recursive call.
UPDATE: I got the debugging to work by copying the Delphi exe to the same directory as the DLL.
I've tried using the ExceptionHandlingScope but it hasn't helped. This is how it looks when it crashes:
The scope has no exception and the errormessage is blank. I tried a few connotations of what was inside the scope but to no avail.
The whole code block is in a try..catch and I've tried wrapping the ExecuteQuery line in it's own try..catch as well, but nothing catches it. The app crashes out every time when I hit continue.
I've also tried putting an execute query before loading the web but it still crashes out.
I'm thinking this has to be something to do with credentials? If I deliberately put the wrong username I get a polite '401 Unauthorized' back and no crash. And if I'm already logged in it doesn't crash either?
After trying the C# test application I tried the same with Delphi XE8 and that also worked, so in the end I've resorted to writing an intermediate DLL in XE8.
I noticed that when importing the TLB from the C# DLL into XE8 it behaved differently from D2007 in that it complained about other missing TLB's when building - notably the system.windows.forms library (and some dependencies). I'm not sure if this has any bearing on XE8 working and D2007 failing, but hopefully it well help anyone else needing a workaround.

Opening a document from Imanage in Word 2016

I am attempting to open an Imanage document, in MS Word, within a temporary test application (for debugging) to later copy over into an ActiveX control project. The error that is popping up is:
Exception thrown at 0x7618851A (msvcrt.dll) in w3wp.exe: 0xC0000005: Access >violation reading location 0x09801000.
If there is a handler for this exception, the program may be safely continued.
The error occurs when running the cmd.Execute line and I am unsure as to why I am getting the error.
using IManage;
using IMANEXTLib;
using System;
namespace WebApplication3
{
public partial class WebForm2 : System.Web.UI.Page
{
IManDatabase imanagedatabase;
IManDMS myDMS = new ManDMSClass();
protected void Page_Load(object sender, EventArgs e)
{
openImanageDoc("docNumber", "versionNumber", "server", "database", ReadOnly);
}
public void imanageLogin(string server, string database)
{
try
{
IManSession session = myDMS.Sessions.Add(server);
IManWorkArea oWorkArea = session.WorkArea;
session.TrustedLogin();
foreach (IManDatabase dbase in session.Databases)
{
if (dbase.Name == database)
{
imanagedatabase = dbase;
}
}
}
catch (Exception ex)
{
throw ex;
}
}
public void openImanageDoc(string docNo, string versionNo, string server, string database, bool isReadOnly = true)
{
IManDocument doc;
try
{
imanageLogin(server, database);
int iDocNo = int.Parse(docNo);
int iVersion = int.Parse(versionNo);
doc = imanagedatabase.GetDocument(iDocNo, iVersion);
openNRTDocument(ref doc, isReadOnly);
imanagedatabase.Session.Logout();
myDMS.Close();
}
catch (Exception Ex)
{
imanagedatabase.Session.Logout();
throw Ex;
}
finally
{
imanagedatabase = null;
myDMS = null;
}
}
public void openNRTDocument(ref IManDocument nrtDocument, Boolean isReadonly)
{
OpenCmd cmd = new OpenCmd();
ContextItems objContextItems = new ContextItems();
objContextItems.Add("NRTDMS", myDMS);
objContextItems.Add("SelectedNRTDocuments", new[] { (NRTDocument)nrtDocument.LatestVersion });
objContextItems.Add("IManExt.OpenCmd.Integration", false);
objContextItems.Add("IManExt.OpenCmd.NoCmdUI", true);
cmd.Initialize(objContextItems);
cmd.Update();
cmd.Execute();
}
}
}
Due to the nature of the error, I am presuming it is a configuration issue rather than a code error although I could be completely wrong as I am very new to programming.
I have found out that w3wp.exe is an IIS worker process created by the app pool but other than that I have no idea what the numeric code represents. Any help or advice is greatly appreciated.
The error is being raised by the OpenCmd instance because it is most likely trying to access resources such as local registry settings. It's not possible to do that in a web application, unless you host your code in a proprietary technology like ActiveX (which is specific to Internet Explorer)
Actually, it is not appropriate for you to use OpenCmd here. Those type of commands (iManage "ICommand" implementations) are intended to be used in regular Windows applications that have either the iManage FileSite or DeskSite client installed. These commands are all part of the so-called Extensibility COM libraries (iManExt.dll, iManExt2.dll, etc) and should not be used in web applications, or at least used with caution as they may inappropriately attempt to access the registry, as you've discovered, or perhaps even display input Win32 dialogs.
For a web app you should instead just limit yourself to the low-level iManage COM library (IManage.dll). This is in fact what iManage themselves do with their own WorkSite Web application
Probably what you should do is replace your openNRTDocument method with something like this:
// create a temporary file on your web server..
var filePath = Path.GetTempFileName();
// fetch a copy of the iManage document and save to the temporary file location
doc.GetCopy(filePath, imGetCopyOptions.imNativeFormat);
In an MVC web application you would then just return a FileContentResult, something like this:
// read entire document as a byte array
var docContent = File.ReadAllBytes(filePath);
// delete temporary copy of file
File.Delete(filePath);
// return byte stream to web client
return File(stream, MediaTypeNames.Application.Octet, fileName);
In a Web Forms application you could do something like this:
// set content disposition as appropriate - here example is for Word DOCX files
Response.ContentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
// write file to HTTP content output stream
Response.WriteFile(filePath);
Response.End();

How do I log the Console output of a hosted scriptcs execution?

I am using scriptcs.net to host dynamic scripts, and I would like to capture the output from Console.WriteLine() into my log as information, and Console.Error.WriteLine() as errors. Same as the way Octopus Deploy custom script logging.
The reason I would like to capture the Console.WriteLine() output is to make it easy for script writers to log information using a familiar tool.
Here is my script hosting function:
public static dynamic RunScript()
{
var logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
var scriptServicesBuilder = new ScriptServicesBuilder(new ScriptConsole(), logger).
LogLevel(LogLevel.Info).Cache(false).Repl(false).ScriptEngine<RoslynScriptEngine>();
var scriptcs = scriptServicesBuilder.Build();
scriptcs.Executor.Initialize(new[] { "System.Web" }, Enumerable.Empty<IScriptPack>());
scriptcs.Executor.AddReferences(Assembly.GetExecutingAssembly());
var result = scriptcs.Executor.Execute("Hello.csx");
scriptcs.Executor.Terminate();
if (result.CompileExceptionInfo != null)
return new { error = result.CompileExceptionInfo.SourceException.Message };
if (result.ExecuteExceptionInfo != null)
return new { error = result.ExecuteExceptionInfo.SourceException.Message };
return result.ReturnValue;
}
The contents of my Hello.csx file:
Console.WriteLine("This output is lost forever");
So my question is how can I capture the text written to Console.WriteLine() and save it?

how to create a fresh database before tests run?

how to create a fresh database (everytime) before tests run from a schema file ?
You can use the SchemaExport class in NHibernate to do this in code:
var schema = new SchemaExport(config);
schema.Drop(true, true);
schema.Execute(true, true, false);
drop the entire database - don't drop table by table - that adds too much maintenance overhead
I have used the following utility methods for running SQL scripts for setting up databases and test data in a project that I am working with every now and then. It has worked rather well:
internal static void RunScriptFile(SqlConnection conn, string fileName)
{
long fileSize = 0;
using (FileStream stream = File.OpenRead(fileName))
{
fileSize = stream.Length;
using (StreamReader reader = new StreamReader(stream))
{
StringBuilder sb = new StringBuilder();
string line = string.Empty;
while (!reader.EndOfStream)
{
line = reader.ReadLine();
if (string.Compare(line.Trim(), "GO", StringComparison.InvariantCultureIgnoreCase) == 0)
{
RunCommand(conn, sb.ToString());
sb.Length = 0;
}
else
{
sb.AppendLine(line);
}
}
}
}
}
private static void RunCommand(SqlConnection connection, string commandString)
{
using (SqlCommand command = new SqlCommand(commandString, connection))
{
try
{
command.ExecuteNonQuery();
}
catch (Exception ex)
{
Console.WriteLine(string.Format("Exception while executing statement: {0}", commandString));
Console.WriteLine(ex.ToString());
}
}
}
I have used the Database Publishing Wizard to generate SQL scripts (and in some cases edited them to include only the data I want to use in the test), and just pass the script file paths into the RunScriptFile method before the tests. The method parses the script file and executes each part that is separated by a GO line separately (I found that this greatly helped in troubleshooting errors that happened while running the SQL scripts).
I has been a while since I wrote the code, but I think it requires the the script file ends with a GO line in order for the last part of it to be executed.
Have a look at these posts.
Ayende Rahien - nhibernate-unit-testing
Scott Muc - unit-testing-domain-persistence-with-ndbunit-nhibernate-and-sqlite
I have found them to be very usefull and basically they are extending the example by Mike Glenn
I use Proteus (Unit Test Utility), available on Google code here :
http://code.google.com/p/proteusproject/
You create a set of data. Each time, you run a unit test, the current data are saved, the set of data is loaded, then you use all the time the same set of data to make your tests. At the end the original data are restored.
Very powerfull
HTH

Categories