web-dev-qa-db-fra.com

Comment puis-je modifier l'emplacement du fichier par programme?

Je suis totalement nouveau sur Log4net.
J'ai réussi à obtenir quelque chose en ajoutant un fichier de configuration et une simple journalisation.
J'ai codé en dur la valeur pour qu'elle soit "C:\temp\log.txt" mais cela ne suffit pas. 

Les journaux doivent aller dans les dossiers spéciaux

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

et ce chemin change selon que vous utilisez Windows Server 2008 ou Windows XP ou Vista, etc.

Comment puis-je simplement modifier l'emplacement du fichier dans log4net par programme?

Voici ce que j'ai fait:

<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?
    }
}

Juste besoin de comprendre comment je peux changer pour enregistrer les choses là où je veux. 

Des suggestions? Merci beaucoup

66
user186134

log4net peut gérer cela pour vous. Toute propriété appender de type chaîne peut être formatée, dans ce cas, en utilisant le gestionnaire log4net.Util.PatternString option. PatternString supporte même le SpecialFolder enum qui permet la config élégante suivante:

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

Voici un test unitaire qui vérifie le pudding:

[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));
}

Le test suivant vérifie que log4net écrit réellement sur le disque (ce qui en fait fondamentalement un test "d'intégration", pas un test unitaire, mais nous en resterons là pour l'instant):

[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"));
}

NB: Je suggère fortement d'utiliser la syntaxe de propriété compact démontrée dans l'exemple ci-dessus. Supprimer tous ces "<property name =" rend votre configuration encore plus lisible.

85
Peter Lillevold

J'ai trouvé une mutation de ce code dans les interwebs:

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;
    }
}

Cela fonctionne pour moi. Notre application doit placer le fichier journal dans un dossier contenant le numéro de version de l'application basé sur le fichier AssemblyInfo.cs.

Vous devriez pouvoir définir logFileLocation par programme (par exemple, vous pouvez utiliser Server.MapPath () s'il s'agit d'une application Web) pour répondre à vos besoins.

38
JackAce

On dirait que la réponse de Peter ne fonctionne pas pour Log4net v1.2.10.0. Une méthode alternative est décrite ici .

Fondamentalement, la méthode consiste à implémenter un convertisseur de modèle personnalisé pour le fichier de configuration log4net.

Ajoutez d'abord cette classe à votre projet:

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));
    }
}

Configurez ensuite le paramètre File de votre FileAppender comme suit:

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

En gros, le %folder lui dit de regarder le convertisseur appelé folder qui le dirige vers la classe SpecialFolderPatternConverter. Il appelle ensuite Convert sur cette classe, en transmettant la valeur enum CommonApplicationData (ou autre).

14
codeulike

Que diriez-vous d'un simple:

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

Pourquoi est-ce si complexe de faire une chose très simple?

6
Eric

Cela a fonctionné pour moi:

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

Si j'ai besoin d'écrire dans des dossiers spéciaux, j'ai trouvé aide ici (2ème et 3ème exemple).

Modifier:

Pour répondre à OP .. Cela fonctionne pour la zone 'tous les utilisateurs':

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

Ce qui correspond normalement à "C:\ProgramData" dans les versions les plus récentes de Windows.

Voir ces aussi:
Comment spécifier un dossier de données d'application commun pour log4net? == https://stackoverflow.com/a/1889591/503621 et commentaires
&
https://superuser.com/q/405097/47628
https://stackoverflow.com/a/5550502/503621

4
bshea

La réponse de JackAce, juste plus concise avec 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

Pour modifier également le chemin du journal des erreurs (en fonction de la réponse de 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

Dans la version actuelle de Log4Net (2.0.8.0), vous pouvez simplement utiliser <file value="${ProgramData}\myFolder\LogFiles\" /> pour C:\ProgramData\.. et ${LocalAppData} pour C:\Users\user\AppData\Local\

1
Apfelkuacha

Excellent cas d'utilisation pour 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();
    }
}

Si le nom de fichier dans app.config est log.txt, la sortie du journal deviendra logs/some_name_log.txt:

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

Pour répondre au problème initial des PO, ce serait:

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

Au lieu d'effectuer cette opération par programme, vous pouvez utiliser des variables d'environnement et des modèles personnalisables dans le fichier de configuration. Voir cette réponse à une question similaire .

Consultez la section "PatternString pour une configuration basée sur un modèle" dans les notes de publication de Log4Net V1.2.10 .

De même, si vous envisagez d'écrire dans un répertoire tel que Enviroment.SpecialFolder.CommonApplicationData, vous devez prendre en compte:

  • Toutes les instances de votre application pour tous les utilisateurs auront-elles un accès en écriture au fichier journal? Par exemple. Je ne crois pas que les non-administrateurs pourront écrire dans Enviroment.SpecialFolder.CommonApplicationData.

  • Conflit si plusieurs instances de votre application (pour le même utilisateur ou des utilisateurs différents) tentent de créer le même fichier. Vous pouvez utiliser le "modèle de verrouillage minimal" (voir  http://logging.Apache.org/log4net/release/config-examples.html permettre à plusieurs processus d'écrire dans le même fichier journal, mais cela aura probablement un impact sur les performances. Ou vous pouvez attribuer à chaque processus un fichier journal différent, par exemple. en incluant l'identifiant de processus dans le nom du fichier en utilisant un modèle personnalisable.

0
Joe

Si vous devez déployer sur des systèmes inconnus et que vous voulez utiliser la solution simple de Philipp M même avec différents dossiers spéciaux, vous pouvez récupérer le chemin de dossier spécial souhaité et définir une variable d'environnement personnalisée avant de charger la configuration log4net . string localData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); Environment.SetEnvironmentVariable("MY_FOLDER_DATA", localData); XmlConfigurator.Configure( ...

... juste pour être sûr que la variable env existe et a la valeur que vous voulez.

0
user3529289