RemotingConfiguration lock my file, how to use log4net's RemotingAppender? - c#

client.cs
RemotingAppender remotingAppender = new RemotingAppender();
remotingAppender.Sink = "tcp://localhost:15642/LoggingSinkInConsoleDaemon";
remotingAppender.BufferSize = 1;
remotingAppender.ActivateOptions();
BasicConfigurator.Configure(remotingAppender);
log.Info("everything is ok!");
server.cs
LogManager.GetRepository().PluginMap.Add(new Plugin.RemoteLoggingServerPlugin("LoggingSinkInConsoleDaemon"));
client.exe log to server.exe, everything is ok, and after client.exe exit, i use Unlocker.exe(which can be found here) find that client.exe is locked by server.exe(which means i can't delete client.exe yet because it is used by server.exe), i locate the error which is caused by RemotingAppender, but i don't know how to resolve.
i think Remoting handle the RemotingAppender's request, and it have locked client.exe, how can i release the lock?
======================update 1===================================
client.cs
var repo = LogManager.GetRepository();
var app = repo.GetAppenders().Where(x => x.GetType() == typeof(RemotingAppender)).FirstOrDefault();
var remotingAppender = app as RemotingAppender;
var root = ((log4net.Repository.Hierarchy.Hierarchy)LogManager.GetRepository()).Root;
var attachable = root as IAppenderAttachable;
attachable.RemoveAppender(remotingAppender);
i tried above it works, but i lose my log.
i search the source, RemoveAppender remove the logger from list, so it doesn't solve the problem

I'm not sure at all but have you tried remotingAppender.Close(); when you finish with it ?

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.

NLog - Why aren't locked files released once Shutdown is called?

I understand NLog's LogManager.Shutdown() to effectively remove all logging items and stop logging. Programmatically deleting a locked log file after this should work, instead I find that it doesn't get deleted immediately, the file is still locked and only gets unlocked after the process ends.
This is my programmatic config:
var fileTarget = new FileTarget()
{
FileName = "logs.txt",
Layout = <string layout>,
KeepFileOpen = true,
ArchiveAboveSize = 50000000,
ArchiveEvery = FileArchivePeriod.Day,
ArchiveNumbering = ArchiveNumberingMode.DateAndSequence,
ArchiveDateFormat = DatePattern
};
var asyncFileTarget =
new NLog.Targets.Wrappers.AsyncTargetWrapper(fileTarget, 10000, NLog.Targets.Wrappers.AsyncTargetWrapperOverflowAction.Block);
asyncFileTarget.Name = "async_target";
var config = new LoggingConfiguration();
config.AddTarget(asyncFileTarget);
config.AddRule(NLog.LogLevel.Debug, NLog.LogLevel.Fatal, asyncFileTarget);
Then if I do,
LogManager.Shutdown(); //logs get flushed and written to the file
File.Delete("logs.txt"); //Nothing happens, file deletes only after process exits
I also tried
LogManager.Configuration = null;
which had the same result.
I need to have the KeepFileOpen = true for performance reasons.
How do I get NLog to release locked target files?
My best guess would be that nLog (which i'm unfamiliar with) is running on another thread. I'm not sure how familiar you are with multithreading, but this would mean that the File.Delete(Logs.txt) code could be executed before LogManager has had a chance to finish shutting down.
Perhaps try using the await keyword on some async functions? Maybe this will help?
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/async
Alternatively, if nLog has a function/indicator like 'IsBusy' or 'isRunning' - you could always use a while loop (after you've called the shutdown)?
Rough Example:
while (nLog.isBusy)
{
// Sleep while we wait for the isBusy status to change
System.Threading.Thread.Sleep(100);
}
When you check NLog's source you could see that a shutdown will close all targets. In the Filetarget that's CloseTarget. That will remove the locks in the FileAppenders.
I create a really small example show it works. You could see it here in Github. Using NLog 4.6.8.
The demo part:
static void Main(string[] args)
{
const string fileName = "C:/temp/logs.txt";
var config = CreateLogConfig(fileName);
LogManager.Configuration = config;
Console.WriteLine("Write logs");
var myLogger = LogManager.GetLogger("myLogger");
myLogger.Info("Hi from my logger!");
LogManager.Flush();// flush to check if really locked. You could disable this and it still works.
WriteConsoleFileExistsAndLockInfo(fileName);
Console.WriteLine("Shutdown NLog");
LogManager.Shutdown(); //logs get flushed and written to the file
WriteConsoleFileExistsAndLockInfo(fileName);
Console.WriteLine($"File content: {File.ReadAllText(fileName)}");
Console.WriteLine($"Full path: {new FileInfo(fileName).FullName}");
Console.WriteLine("Deleting file ...");
File.Delete(fileName);
Console.WriteLine("Deleted file");
WriteConsoleFileExistsAndLockInfo(fileName);
}
The output:
If it still isn't working at your site, check NLog's internal log: <nlog internalLogFile="c:\log.txt" internalLogLevel="Trace">
Of course it could be another process is locking your file. Maybe antivirus software?

Tfs Check-in using PendAdd: The array must contain at least one element

So I'm having a problem with automating my code to check-in files to TFS, and it's been driving me up the wall! Here is my code:
string location = AppDomain.CurrentDomain.BaseDirectory;
TfsTeamProjectCollection baseUserTpcConnection = new TfsTeamProjectCollection(uriToTeamProjectCollection);
IIdentityManagementService ims = baseUserTpcConnection.GetService<IIdentityManagementService>();
TeamFoundationIdentity identity = ims.ReadIdentity(IdentitySearchFactor.AccountName, #"PROD1\JR", MembershipQuery.None, ReadIdentityOptions.None);
TfsTeamProjectCollection impersonatedTpcConnection = new TfsTeamProjectCollection(uriToTeamProjectCollection, identity.Descriptor);
VersionControlServer sourceControl = impersonatedTpcConnection.GetService<VersionControlServer>();
Workspace workspace = sourceControl.CreateWorkspace("MyTempWorkspace", sourceControl.AuthorizedUser);
String topDir = null;
try
{
Directory.CreateDirectory(location + "TFS");
String localDir = location + "TFS";
workspace.Map("$/Automation/", localDir);
workspace.Get();
destinationFile = Path.Combine(localDir, Name + ".xml");
string SeconddestinationFile = Path.Combine(localDir, Name + ".ial");
bool check = sourceControl.ServerItemExists(destinationFile, ItemType.Any);
PendingChange[] pendingChanges;
File.Move(sourceFile, destinationFile);
File.Copy(destinationFile, sourceFile, true);
File.Move(SecondsourceFile, SeconddestinationFile);
File.Copy(SeconddestinationFile, SecondsourceFile, true);
if (check == false)
{
workspace.PendAdd(localDir,true);
pendingChanges = workspace.GetPendingChanges();
workspace.CheckIn(pendingChanges, Comments);
}
else
{
workspace.PendEdit(destinationFile);
pendingChanges = workspace.GetPendingChanges();
workspace.CheckIn(pendingChanges, Comments);
}
and the problem is that whenever it's NEW files (PendEdit works correctly when the files already exist in TFS) that my code is attempting to check in, and it runs through this code:
if (check == false)
{
workspace.PendAdd(localDir,true);
pendingChanges = workspace.GetPendingChanges();
workspace.CheckIn(pendingChanges, Comments);
}
The files, instead of being in the included changes in pending changes, are instead in the excluded changes like so:
and when the line that actually does the check-in runs, I'll get a "The array must contain at least one element" error, and the only way to fix it is to manually add those detected changes, and promote them to included changes, and I simply can't for the life of me figure out how to do that programatically though C#. If anyone has any guidance on what direction I should take for this, I would really appreciate it! Thank you!
Edit: I've also discovered another way to solve this by reconciling the folder, which also promotes the detected changes, but again the problem is I can't seem to figure out how to program that to do it automatically.
I know that running the visual studio developer command prompt, redirecting to the folder that this mapping is in, and the running "tf reconcile /promote" is one way, but I can only automate that as far as the /promote part, because that brings up a toolbox that a user would have to input into, which defeats the purpose of the automation. I'm at a loss.
Next Edit in response to TToni:
Next Edit in response to TToni:
I'm not entirely sure if I did this CreateWorkspaceParameters correctly (see picture 1), but this time it gave the same error, but the files were not even in the excluded portions. They just didn't show up anywhere in the pending changes (see picture 2).
Check this blog:
The workspace has a method GetPendingChangesWithCandidates, which actually gets all the “Excluded” changes. Code snippet is as below:
private void PendChangesAndCheckIn(string pathToWorkspace)
{
//Get Version Control Server object
VersionControlServer vs = collection.GetService(typeof
(VersionControlServer)) as VersionControlServer;
Workspace ws = vs.TryGetWorkspace(pathToWorkspace);
//Do Delete and Copy Actions to local path
//Create a item spec from the server Path
PendingChange[] candidateChanges = null;
string serverPath = ws.GetServerItemForLocalItem(pathToWorkspace);
List<ItemSpec> its = new List<ItemSpec>();
its.Add(new ItemSpec(serverPath, RecursionType.Full));
//get all candidate changes and promote them to included changes
ws.GetPendingChangesWithCandidates(its.ToArray(), true,
out candidateChanges);
foreach (var change in candidateChanges)
{
if (change.IsAdd)
{
ws.PendAdd(change.LocalItem);
}
else if (change.IsDelete)
{
ws.PendDelete(change.LocalItem);
}
}
//Check In all pending changes
ws.CheckIn(ws.GetPendingChanges(), "This is a comment");
}

ServerManager.CommitChanges( ) throws Error: "Cannot write configuration file"

Here is my code to create an application
public static bool CreateApplication(String websiteName, String applicationName, String AppDIR,String appPoolName)
{
try
{
var windowsDir = Environment.GetEnvironmentVariable("SystemRoot");
Process.Start(windowsDir+#"\system32\inetsrv\appcmd","unlock config -section:system.webServer/security/authentication/windowsAuthentication");
Process.Start(windowsDir+#"\system32\inetsrv\appcmd","unlock config -section:system.webServer/security/authentication/anonymousAuthentication");
ServerManager iisManager = new ServerManager();
if (!applicationName.Contains("/"))
applicationName = "/" + applicationName;
var app = iisManager.Sites[websiteName].Applications.Add(applicationName, AppDIR);
app.ApplicationPoolName = appPoolName;
var config = app.GetWebConfiguration();
var anonsection = config.GetSection("system.webServer/security/authentication/anonymousAuthentication", iisManager.Sites[websiteName].Name + applicationName);
anonsection["enabled"] = false;
var winsection = config.GetSection("system.webServer/security/authentication/windowsAuthentication", iisManager.Sites[websiteName].Name + applicationName);
winsection["enabled"] = true;
iisManager.CommitChanges(); //Blows up here
return true;
}
catch
{
return false;
}
}
Everything is fine until it hit's the commit changes method call.
It throws this error
Error: Cannot write configuration file
All of the code is verified as working except for where I change the enabled values to false and true.
When I added these in it started to explode.
Is there any way to fix this from code, that can be distributed to other machines?
Update:
After re-locking the files
like this
Process.Start(windowsDir + #"\system32\inetsrv\appcmd", "lock config -section:system.webServer/security/authentication/windowsAuthentication");
Process.Start(windowsDir + #"\system32\inetsrv\appcmd", "lock config -section:system.webServer/security/authentication/anonymousAuthentication");
I now get this error
Error: Cannot commit configuration changes because the file has
changed on disk
I also ran into the second of your two issues (Error: Cannot commit configuration changes because the file has changed on disk) for our c# application which we use to configure IIS servers as part of our deploy operation.
For me in the end it was unintentionally nesting two using statements wrapped around the ServerManager object that caused the problem.
For your particular situation I would try to add a using statement for your reference to ServerManager and ensure that the command line appcmd calls are outside the using statement or moved within the using statement but translated into API calls via the ServerManager object.

WUA IUpdate2.CopyToCache throws WU_E_INVALID_UPDATE_TYPE

I have some problems getting the WUA API to run as expected.
I want to manually copy a update file to a machine and then feed it to the WUA.
The call IUpdate2.CopyToCache causes an error that confuses me slightly. The update is definitively correct. Using a file name that does not even exist also results in the same error!
There is another strange thing I noticed: I searched for an update and found it in the API, but writing it to disk does not work at all. The code executes and there is no error reported, but the directory stays empty.
IUpdateSearcher updateSearcher = new UpdateSearcher();
updateSearcher.Online = true;
ISearchResult searchResult = updateSearcher.Search("UpdateID='" + wsusID + "'");
if (searchResult.ResultCode == OperationResultCode.orcSucceeded && searchResult.Updates.Count == 1)
{
IUpdate update = searchResult.Updates[0];
Console.WriteLine(update.KBArticleIDs[0]);
Console.WriteLine(update.Title);
Console.WriteLine(update.Identity.UpdateID);
Console.WriteLine("IsInstalled=" + update.IsInstalled);
Console.WriteLine("IsDownloaded=" + update.IsDownloaded);
// this line does nothing
update.CopyFromCache("C:\\Test\\", true);
// this returns error code 0
int errorCode = Marshal.GetLastWin32Error();
var update2 = (IUpdate2)update;
var s = new StringCollection();
// this file has been manually downloaded and exists!
s.Add(#"C:\test\Windows6.1-KB2518869-x64.msu");
// this throws the exception (0x80240026 - WU_E_INVALID_UPDATE_TYPE)
update2.CopyToCache(s);
}
Why is the CopyFromCache not doing anything and why is CopyToCache throwing this weird Exception, even if the file does not exist?
API reference: http://msdn.microsoft.com/en-us/library/windows/desktop/aa386101(v=VS.85).aspx
The problem with your code is that the specified update is not a real update.
It is a container for a bundle of updates. Try to copy the file to the cache for the bundled update.
// Example
Console.WriteLine("Bundled update=" + update.BundledUpdates[0].Title);
var s = new StringCollection();
s.Add(#"C:\test\Windows6.1-KB2518869-x64.msu");
((IUpdate2)update.BundledUpdates[0]).CopyToCache(s);

Categories