web-dev-qa-db-de.com

Wie kann ich den Dateispeicherort programmgesteuert ändern?

Ich bin neu bei Log4net.
Ich habe es geschafft, etwas hinzuzufügen, indem ich eine Konfigurationsdatei hinzufügte und einfach protokollierte.
Ich habe den Wert hart codiert, um "C:\temp\log.txt" zu sein, aber das ist nicht gut genug. 

Die Protokolle müssen in die speziellen Ordner gehen

path = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);

dieser Pfad ändert sich abhängig davon, ob Sie Windows Server 2008 oder Windows XP oder Vista usw. verwenden.

Wie kann ich den Speicherort der Datei in log4net programmgesteuert ändern?

Das habe ich getan:

<configSections>
<section name="log4net"
         type="log4net.Config.Log4NetConfigurationSectionHandler,Log4net"/>
</configSections>
<log4net>         
    <root>
        <level value="DEBUG" />
        <appender-ref ref="LogFileAppender" />
    </root>
    <appender name="LogFileAppender" type="log4net.Appender.RollingFileAppender">
        <param name="File" value="C:\temp\log.txt" />
        <param name="AppendToFile" value="true" />
        <rollingStyle value="Size" />
        <maxSizeRollBackups value="10" />
        <maximumFileSize value="10MB" />
        <staticLogFileName value="true" />
        <layout type="log4net.Layout.PatternLayout">
            <param name="ConversionPattern" value="%-5p%d{yyyy-MM-dd hh:mm:ss} – %m%n" />
        </layout>
    </appender>
</log4net>

class Program
{
    protected static readonly ILog log = LogManager.GetLogger(typeof(Program));

    static void Main(string[] args)
    {
        log4net.Config.XmlConfigurator.Configure();
        log.Warn("Log something");

        path = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);


        // How can I change where I log stuff?
    }
}

Ich muss nur herausfinden, wie ich das Protokoll an den gewünschten Ort ändern kann. 

Irgendwelche Vorschläge? Vielen Dank

66
user186134

log4net kann dies für Sie erledigen. Jede Appender-Eigenschaft des Typs string kann in diesem Fall mit dem Optionshandler log4net.Util.PatternString formatiert werden. PatternString unterstützt sogar das SpecialFolder enum, das die folgende elegante Konfiguration ermöglicht:

<appender name="LogFileAppender" type="log4net.Appender.RollingFileAppender" >
    <file type="log4net.Util.PatternString" 
        value="%envFolderPath{CommonApplicationData}\\test.txt" />
    ...
</appender>

Hier ist ein Unit-Test, der den Pudding beweist:

[Test]
public void Load()
{
    XmlConfigurator.Configure();
    var fileAppender = LogManager.GetRepository()
        .GetAppenders().First(appender => appender is RollingFileAppender);

    var expectedFile = 
        Path.Combine(
            Environment.GetFolderPath(
                Environment.SpecialFolder.CommonApplicationData),
                "test.txt");

    Assert.That(fileAppender, 
        Is.Not.Null & Has.Property("File").EqualTo(expectedFile));
}

Der folgende Test prüft, ob log4net tatsächlich auf die Festplatte schreibt (was im Grunde genommen einen "Integrationstest" und nicht einen Komponententest macht, aber wir lassen es für jetzt).

[Test]
public void Log4net_WritesToDisk()
{
    var expectedFile = 
        Path.Combine(
            Environment.GetFolderPath(
                Environment.SpecialFolder.CommonApplicationData),
                "test.txt");

    if (File.Exists(expectedFile))
        File.Delete(expectedFile);

    XmlConfigurator.Configure();

    var log = LogManager.GetLogger(typeof (ConfigTest));
    log.Info("Message from test");

    LogManager.Shutdown();

    Assert.That(File.ReadAllText(expectedFile), 
        Text.Contains("Message from test"));
}

Hinweis: Ich empfehle dringend die Verwendung der kompakten Eigenschaftssyntax, die im obigen Beispiel gezeigt wird. Wenn Sie alle diese "<property name =" entfernen, wird Ihre Konfiguration deutlich lesbarer.

85
Peter Lillevold

Ich habe eine Mutation dieses Codes in den Interwebs gefunden:

XmlConfigurator.Configure();
log4net.Repository.Hierarchy.Hierarchy h =
(log4net.Repository.Hierarchy.Hierarchy) LogManager.GetRepository();
foreach (IAppender a in h.Root.Appenders)
{
    if (a is FileAppender)
    {
        FileAppender fa = (FileAppender)a;
        // Programmatically set this to the desired location here
        string logFileLocation = @"C:\MySpecialFolder\MyFile.log";

        // Uncomment the lines below if you want to retain the base file name
        // and change the folder name...
        //FileInfo fileInfo = new FileInfo(fa.File);
        //logFileLocation = string.Format(@"C:\MySpecialFolder\{0}", fileInfo.Name);

        fa.File = logFileLocation;
        fa.ActivateOptions();
        break;
    }
}

Das funktioniert für mich. Unsere Anwendung muss die Protokolldatei in einem Ordner ablegen, der die Versionsnummer der Anwendung enthält, die auf der Datei AssemblyInfo.cs basiert.

Sie sollten in der Lage sein, logFileLocation programmgesteuert festzulegen (z. B. können Sie Server.MapPath () verwenden, wenn es sich um eine Webanwendung handelt), um Ihre Anforderungen zu erfüllen.

38
JackAce

Sieht aus wie Peters Antwort funktioniert nicht für Log4net v1.2.10.0. Eine alternative Methode wird beschrieben hier .

Grundsätzlich besteht die Methode darin, einen benutzerdefinierten Musterkonverter für die log4net-Konfigurationsdatei zu implementieren.

Fügen Sie diese Klasse zunächst Ihrem Projekt hinzu:

public class SpecialFolderPatternConverter : log4net.Util.PatternConverter
{
    override protected void Convert(System.IO.TextWriter writer, object state)
    {
        Environment.SpecialFolder specialFolder = (Environment.SpecialFolder)Enum.Parse(typeof(Environment.SpecialFolder), base.Option, true);
        writer.Write(Environment.GetFolderPath(specialFolder));
    }
}

Dann richten Sie den File-Parameter Ihres FileAppender wie folgt ein:

<file type="log4net.Util.PatternString">
    <converter>
      <name value="folder" />
      <type value="MyAppName.SpecialFolderPatternConverter,MyAppName" />
    </converter>
    <conversionPattern value="%folder{CommonApplicationData}\\SomeOtherFolder\\log.txt" />
  </file>

Grundsätzlich fordert %folder den Konverter namens folder auf, der ihn auf die SpecialFolderPatternConverter-Klasse verweist. Dann ruft sie Convert für diese Klasse auf und übergibt den Enumment-Wert CommonApplicationData (oder was auch immer).

14
codeulike

Wie wäre es mit einem einfachen:

XmlConfigurator.LogFullFilename = @"c:\ProgramData\MyApp\Myapp.log";

Warum ist es so komplex, eine wirklich einfache Sache zu machen?

6
Eric

Das hat für mich funktioniert:

  <log4net>
    <appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
..
      <file value="${APPDATA}\MyApp\MyApp Client\logs\Log.txt"/>
..
  </log4net>

Wenn Sie in spezielle Ordner schreiben müssen, habe ich Hilfe hier (2. und 3. Beispiel) gefunden.

Bearbeiten:

Beantworten von OP .. Dies funktioniert für den Bereich "Alle Benutzer":

      ...
      <file value="${ALLUSERSPROFILE}\MyApp\MyApp Client\logs\Log.txt"/>
      ...

In neueren Windows-Versionen normalerweise "C:\ProgramData".

Siehe auch diese:
Wie lege ich einen allgemeinen Anwendungsdatenordner für log4net fest? == https://stackoverflow.com/a/1889591/503621 und Kommentare
&
https://superuser.com/q/405097/47628
https://stackoverflow.com/a/5550502/503621

4
bshea

JackAce's Antwort, genauer gesagt mit Linq:

c #

XmlConfigurator.Configure();
var appender = (LogManager.GetRepository() as Hierarchy).Root.Appenders
    .OfType<FileAppender>()
    .First();

appender.File = logPath;
appender.ActivateOptions();

VB.NET

XmlConfigurator.Configure()
Dim appender = CType(LogManager.GetRepository(), Hierarchy).Root.Appenders _
    .OfType(FileAppender)() _
    .First()

appender.File = logPath
appender.ActivateOptions()
3
jmjohnson85

So ändern Sie auch den Pfad des Fehlerprotokolls (basierend auf der Antwort von JackAce):

private static void SetLogPath(string path, string errorPath)
{
    XmlConfigurator.Configure();
    log4net.Repository.Hierarchy.Hierarchy h =
    (log4net.Repository.Hierarchy.Hierarchy)LogManager.GetRepository();
    foreach (var a in h.Root.Appenders)
    {
        if (a is log4net.Appender.FileAppender)
        {
            if (a.Name.Equals("LogFileAppender"))
            { 
                log4net.Appender.FileAppender fa = (log4net.Appender.FileAppender)a;                    
                string logFileLocation = path; 
                fa.File = logFileLocation;                   
                fa.ActivateOptions();
            }
            else if (a.Name.Equals("ErrorFileAppender"))
            {
                log4net.Appender.FileAppender fa = (log4net.Appender.FileAppender)a;
                string logFileLocation = errorPath;
                fa.File = logFileLocation;
                fa.ActivateOptions();
            }
        }
    }
}
3
Jim109

In der aktuellen Version von Log4Net (2.0.8.0) können Sie einfach <file value="${ProgramData}\myFolder\LogFiles\" /> für C:\ProgramData\.. und ${LocalAppData} für C:\Users\user\AppData\Local\ verwenden.

1
Apfelkuacha

Toller Anwendungsfall für LINQs OfType<T> filter:

/// <summary>
/// Applies a transformation to the filenames of all FileAppenders.
/// </summary>
public static void ChangeLogFile(Func<string,string> transformPath)
{
    // iterate over all FileAppenders
    foreach (var fileAppender in LogManager.GetRepository().GetAppenders().OfType<FileAppender>())
    {
        // apply transformation to the filename
        fileAppender.File = transformPath(fileAppender.File);
        // notify the logging subsystem of the configuration change
        fileAppender.ActivateOptions();
    }
}

Wenn der Dateiname in der app.config log.txt lautet, ändert sich die Protokollausgabe in logs/some_name_log.txt:

ChangeLogFile(path => Path.Combine("logs", $"some_name_{Path.GetFileName(path)}"));

Um das ursprüngliche Problem der OPs zu beantworten, wäre das:

ChangeLogFile(path => Path.Combine(
    Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), path));
1
Gigo

Alternativ zum Programm können Sie Umgebungsvariablen und anpassbare Muster in der Konfigurationsdatei verwenden. Siehe diese Antwort auf eine ähnliche Frage .

Siehe "PatternString für musterbasierte Konfiguration" in den Versionshinweisen zu Log4Net V1.2.10 .

Wenn Sie darüber nachdenken, in ein Verzeichnis wie Enviroment.SpecialFolder.CommonApplicationData zu schreiben, müssen Sie Folgendes beachten:

  • Haben alle Instanzen Ihrer Anwendung für alle Benutzer Schreibzugriff auf die Protokolldatei? Z.B. Ich glaube nicht, dass Nicht-Administratoren an Enviroment.SpecialFolder.CommonApplicationData schreiben können.

  • Konflikt, wenn mehrere Instanzen Ihrer Anwendung (für denselben oder verschiedene Benutzer) dieselbe Datei versuchen. Sie können das "minimale Sperrmodell" verwenden (siehe  http://logging.Apache.org/log4net/release/config-examples.html Damit mehrere Prozesse in dieselbe Protokolldatei schreiben können, wird dies jedoch Auswirkungen auf die Leistung haben. Oder Sie können jedem Prozess eine andere Protokolldatei zuweisen, z. indem Sie die Prozess-ID mithilfe eines anpassbaren Musters in den Dateinamen aufnehmen.

0
Joe

Wenn Sie auf unbekannten Systemen implementieren müssen und die einfache Lösung von Philipp M auch mit verschiedenen speziellen Ordnern verwenden möchten, können Sie den gewünschten speziellen Ordnerpfad abrufen und eine benutzerdefinierte env-Variable festlegen, bevor Sie die log4net-Konfiguration laden string localData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); Environment.SetEnvironmentVariable("MY_FOLDER_DATA", localData); XmlConfigurator.Configure( ...

... um sicherzugehen, dass die env-Variable existiert und den gewünschten Wert hat.

0
user3529289