Anterior Próximo

Criando um appender com zip para o log4netpublicado em 13/01/2010

Esta semana no trabalho, tive a necessidade de criar um expurgo para os logs de um determinado projeto.

Os logs deste projeto são escritos em arquivos txt e rotacionados diariamente, o expurgo teria que funcionar zipando e movendo os arquivos para um outro servidor sempre que a semana mudasse.

Minha primeira idéia foi criar um executável que rodaria sempre que uma semana terminasse e ziparia e moveria os arquivos antigos para o servidor de histórico, mas após uma conversa com meu amigo Marcelo Teixeira fui convencido de criar um novo appender para o log4net que fizesse esse trabalho todo.

Para salvar os logs e mudar os arquivos sempre que o dia terminasse ou que o tamanho do arquivo atingisse 20MB, estavámos utilizando o log4net e seu appender RollingFileAppender.

A idéia inicial era simples, fazer uma classe que herdasse de RollingFileAppender e reescrever o método responsável pelo rotacionamento para que ele também zipasse e movesse os arquivos antigos.

Ao iniciar o desenvolvimento seguindo essa linha percebi que o log4net possuía um bug.

Para definirmos que o arquivo será rotacionado de (dia/dia, mes/mes, ano/ano, hora/hora etc) necessitamos configurar a propriedade datePattern na configuração do appender, e ao tentar colocar <datePattern value="w"/> ( padrão de semana do mês ) percebi que nada funcionava.

Não funcionava pois o método ComputeCheckPeriod do log4net tenta converter uma data para string utilizando o pattern passado e o .net não tem nenhum pattern que represente as semanas do mês !!!

private RollPoint ComputeCheckPeriod(string datePattern)
{
	// Get string representation of base line date
	string r0 = s_date1970.ToString(datePattern, System.Globalization.DateTimeFormatInfo.InvariantInfo);

Fiquei meio decepcionado e parti para a correção do problema. Tinha diversas possibilidades como:

  • Criar meu próprio padrão de datas que contemplasse também as semanas.
  • Mudar a maneira de identificação de patterns da classe.
  • Criar uma propriedade diferente para definir que teríamos um rotacionamento semanal.

Entre essas possibilidades, escolhi criar uma propriedade diferente para definir que teriamos um rotacionamento semanal pois o desenvolvimento da mesma seria mais rápido e meu prazo estava curto.

Alterei então o método ComputeCheckPeriod para que se a propriedade Week ( criada por mim ) fosse true, retornasse um RollPoint.TopOfWeek indicando assim um rotacionamento semanal.

// Check if the string representations are different
if (r0 != null && r1 != null && !r0.Equals(r1))
{
    // Found highest precision roll point
    // [UPDATE] - Force the weekly rotation since. NET does not support any weekly date pattern.
    if (Week)
        return RollPoint.TopOfWeek;
    else
        return (RollPoint)i;
}

Para zipar os arquivos antigos, alterei o método AdjustFileBeforeAppend para que zipasse os arquivos antigos sempre que um rotacionamento por data fosse realizado.

virtual protected void AdjustFileBeforeAppend()
{
    if (m_rollDate)
    {
        DateTime n = m_dateTime.Now;
        if (n >= m_nextCheck)
        {
            m_now = n;
            m_nextCheck = NextCheckDate(m_now, m_rollPoint);

            RollOverTime(true);

            // [UPDATE] - Zip All the old files when the log rolls up             
            this.ZipOldFiles();
        }
    }

    if (m_rollSize)
    {
        if ((File != null) && ((CountingQuietTextWriter)QuietWriter).Count >= m_maxFileSize)
        {
            RollOverSize();
        }
    }
}

Método responsável por zipar os arquivos antigos:

/// 
/// [UPDATE]  
/// Zip All the old files when the log rolls up
/// 
private void ZipOldFiles()
{
    string zipDir = this.ZipPath.Substring(0, this.ZipPath.LastIndexOf("\\"));
    if (Directory.Exists(zipDir) && IsZipFile(this.ZipPath))
    {
        string originalPath = File.Substring(0, File.LastIndexOf("\\"));
        string[] logFiles = Directory.GetFiles(originalPath);

        using (ZipFile zip = new ZipFile())
        {
            DateTime smallerDate = m_nextCheck;

            // ziping files files...
            foreach (string logFile in logFiles)
            {
                // Gets all the different files of the current file with the same prefix and are not zip
                if (logFile != File && logFile.IndexOf(this.m_baseFileName).Equals(0) && !IsZipFile(logFile))
                {
                    zip.AddFile(logFile);

                    // Getting the date of the older file to put in zip file name...
                    FileInfo fInfo = new FileInfo(logFile);
                    if (fInfo.CreationTime < smallerDate)
                        smallerDate = fInfo.CreationTime;
                }
            }

            // saving the zip file...
            if (zip.Entries.Count > 0)
                zip.Save(this.ZipPath.Replace(".zip", smallerDate.ToString("-dd-MM-yyyy") + ".zip"));
            
            // removing files...
            foreach (string logFile in logFiles)
            {
                // Gets all the different files of the current file with the same prefix and are not zip
                if (logFile != File && logFile.IndexOf(this.m_baseFileName).Equals(0) && !IsZipFile(logFile))
                    System.IO.File.Delete(logFile);
            }
        }
    }
    else
        throw new ArgumentException("Invalid zip path. The property zipPath must be setted in the configuration file.");
}

A configuração deste apender para que rotacione e zipe os arquivos semanalmente deve ser feita da seguinte forma:

<appender name="RollingLogFileAppender" type="Ajustes.Log.Appender.RollingFileAndZipAppender">
	<file value="c:\\logs\\Arquivo.log"/>
	<staticLogFileName value="false"/>
	<appendToFile value="true"/>
	<rollingStyle value="Composite"/>
	<datePattern value="yyyyMMdd"/>
	<week value="true"/>
	<zipPath value="c:\\Historico.zip"/>
	<maxSizeRollBackups value="150"/>
	<maximumFileSize value="500KB"/>
	<layout type="log4net.Layout.PatternLayout">
		<param name="ConversionPattern" value="%d [%t][%u] %-5p %c %m%n"/>
	</layout>
	<lockingModel type="log4net.Appender.FileAppender+MinimalLock"/>
</appender>

Vale lembrar que se não definirmos a propriedade week, o appender funcionará normalmente, fazendo o rotacionamento e zipando os arquivos de acordo com o date pattern passado.

O único ponto triste disso tudo é que como alguns métodos que alterei não estavam marcados como virtual, não pude reescreve-los e por isso tive que copiar todo o conteúdo da classe RollingFileAppender para fazer as alterações.

Poderia também ter baixado o código fonte do log4net, marcado os métodos como virtual e trabalhado em cima deles com herança mas um dos meus requisitos era não alterar a dll original do log4net.

Abaixo você poderá fazer o download da classe, espero que seja útil. Todas as alterações feitas por mim estão marcadas com a palavra UPDATE nos comentários.


Tags:

retweet

BBCode permitido - [code], [pre], [b], [i], [u], [del], [url], [left], [right], [center]

  • 28/12/2011 15:02 - CArlos

    Hi! Thank you for that excellent post, it was very useful.

    As spaniard, I can understand most of what you explain, but i think that you will have lot of more visits writting in English.

    Thanks aniway :)


Ir direto para o topo