Class1.cs :  » Logging-Tools » CSharp-Dot-Net-Logger » VS » Logger » C# / CSharp Open Source

Home
C# / CSharp Open Source
1.2.6.4 mono .net core
2.2.6.4 mono core
3.Aspect Oriented Frameworks
4.Bloggers
5.Build Systems
6.Business Application
7.Charting Reporting Tools
8.Chat Servers
9.Code Coverage Tools
10.Content Management Systems CMS
11.CRM ERP
12.Database
13.Development
14.Email
15.Forum
16.Game
17.GIS
18.GUI
19.IDEs
20.Installers Generators
21.Inversion of Control Dependency Injection
22.Issue Tracking
23.Logging Tools
24.Message
25.Mobile
26.Network Clients
27.Network Servers
28.Office
29.PDF
30.Persistence Frameworks
31.Portals
32.Profilers
33.Project Management
34.RSS RDF
35.Rule Engines
36.Script
37.Search Engines
38.Sound Audio
39.Source Control
40.SQL Clients
41.Template Engines
42.Testing
43.UML
44.Web Frameworks
45.Web Service
46.Web Testing
47.Wiki Engines
48.Windows Presentation Foundation
49.Workflows
50.XML Parsers
C# / C Sharp
C# / C Sharp by API
C# / CSharp Tutorial
C# / CSharp Open Source » Logging Tools » CSharp Dot Net Logger 
CSharp Dot Net Logger » VS » Logger » Class1.cs
/* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 2003 Validity Systems Inc.  All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in
*    the documentation and/or other materials provided with the
*    distribution.
*
* 3. The end-user documentation included with the redistribution, if
*    any, must include the following acknowlegement:
*       "This product includes software developed by the
*        Validity Systems Inc."
*    Alternately, this acknowlegement may appear in the software itself,
*    if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "#Logger", "CS Logger", and "Validity Systems
*    Inc." must not be used to endorse or promote products derived
*    from this software without prior written permission. For written
*    permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "#Logger"
*    nor may "#Logger" appear in their names without prior written
*    permission of Validity Systems Inc.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Validity Systems Inc.
*
*
*/

/*
 *  Author: Athony La Forge (Validity Systems Inc.) 
 */

using System;
using System.Collections;
using System.Threading;
using System.IO;

namespace VS.Logger{
  /// <summary>
  /// Represents a log message
  /// </summary>
  public struct LoggerMessage
  {
    public int level;
    public string level_desc;
    public string tag;
    public string message;
    public long time;
  }

  /// <summary>
  /// Abstract class responsible managing (in the form or a queue) and dispatching log messages
  /// to their appropriate source.  The basic premises is that everything from a file writers, 
  /// to an e-mailer, to an syslog interface, to a db interface, etc... could be implemented and the only
  /// thing the implementer would need to worry about was the interface.
  /// </summary>
  public abstract class LoggerEventHandler
  {
    private bool alive = false;
    private Queue q = null;
    private Thread dispatch = null;
    protected string[] logLevelDescriptors = null;

    public LoggerEventHandler()
    {
      q = Queue.Synchronized(new Queue(1000));
      start();
    }

    /// <summary>
    /// Starts the execution of the Handler (e.g. Queue goes live)
    /// </summary>
    public void start()
    {
      //It's already alive, nothing to do
      if (alive) return;

      //Make sure this is the only place where alive is set to true
      alive = true;
      dispatch = new Thread(new ThreadStart(dispatchMessages));
      dispatch.Start();
    }

    /// <summary>
    /// Stops the execution of the Handler gracefully (e.g. everything in the queue is
    /// dispatched, but nothing can be added to it)
    /// </summary>
    public void shutdown()
    {
      //Nothing to do
      if (!alive) return;

      alive = false;
      Monitor.Enter(q);
      Monitor.PulseAll(q);
      Monitor.Exit(q);
    }

    /// <summary>
    /// Will immediately shutdown the thread without cleaning up or clearing
    /// out the queue.  Consequently this is not the recommended way to terminate.
    /// </summary>
    public void abort()
    {
      if (!alive) return;

      alive = false;
      dispatch.Abort();
    }

    /// <summary>
    /// This is the worker method where the messages get dispatched
    /// </summary>
    protected void dispatchMessages()
    {
      //int max = 0;
      while (alive)
      {
        while((q.Count != 0) && alive)
        {
          //if(q.Count > max)max = q.Count;
          log((LoggerMessage)q.Dequeue());
        }

        if ((alive) && (q.Count == 0))
        {
          Monitor.Enter(q);
          if (q.Count == 0) Monitor.Wait(q);
          Monitor.Exit(q);
        }
      }

      //You will only reach this code if you are exiting the program
      //This block ensures the messages that were queued up will get dumped
      while (q.Count != 0)
      {
        log((LoggerMessage)q.Dequeue());
      }

      //Do any special shutdown you need to do
      onShutdown();

      //Remove thread reference so you don't have object leak
      dispatch = null;
    }

    /// <summary>
    /// Responsible for queuing the log message
    /// </summary>
    /// <param name="tag"></param>
    /// <param name="level"></param>
    /// <param name="level_desc"></param>
    /// <param name="message"></param>
    public void log(string tag, int level, string level_desc, string message)
    {
      if (!alive) return;

      LoggerMessage lm = new LoggerMessage();
      lm.message = message;
      lm.tag = tag;
      lm.level = level;
      lm.level_desc = level_desc;
      lm.time = System.DateTime.Now.ToFileTime();

      q.Enqueue(lm);

      Monitor.Enter(q);
      Monitor.PulseAll(q);
      Monitor.Exit(q);
    }

    /// <summary>
    /// User implementable log method (e.g. what actions they need to take to perform
    /// a "log" operation, like logging to a file for example).
    /// </summary>
    /// <param name="message"></param>
    protected abstract void log(LoggerMessage message);

    /// <summary>
    /// User implementable, meant for cleanup when the thread is stopped (e.g. if you need to
    /// close files, db connections, etc...)
    /// </summary>
    protected abstract void onShutdown();
  }


  /// <summary>
  /// Test class, by no means a unit test, but more for experimenting
  /// </summary>
  class Class1
  {
    [STAThread]
    static void Main(string[] args)
    {
      //Logger logger = new Logger(3, "test.txt");
      Logger logger = Logger.singleton();
      logger.addSpecialLogger(2, "test2.txt");


      for(int x = 0; x < 100; x++)
      {
        for(int i = 0; i < 100; i++)
        {
          logger.log(i%6, "TEST", "message " + i);
        }
        //Thread.Sleep(500);
      }
      System.Console.WriteLine("{0}",DateTime.Now.ToFileTime()/10000);
//      logger.log(0, "TEST", "message 1");
//      logger.log(1, "TEST", "message 2");
//      logger.log(2, "TEST", "message 3");
      //System.Console.ReadLine();
      logger.shutdown();
    }
  }

  /// <summary>
  /// Simple To File Logger.  All messages a written out to a flat text file.
  /// </summary>
  public class BasicFileLogEventHandler : LoggerEventHandler
  {
    StreamWriter stream = null;
    bool append = true;

    public BasicFileLogEventHandler(String filename)
    {
      if (filename != null)
      {
        FileMode fm;

        if (append)fm=FileMode.Append;
        else fm=FileMode.Create;

        FileStream fs = new FileStream(filename, fm, FileAccess.Write, FileShare.Read);

        //file = new StreamWriter(filename);

        stream = new StreamWriter(fs, System.Text.Encoding.UTF8, 4096);
        //stream = new StreamWriter(fs, System.Text.Encoding.UTF8, 1024);
      }
    }

    override protected void log(LoggerMessage message)
    {
      if (stream == null) return;
      string time = System.DateTime.FromFileTime(message.time).ToString();
      string test = "[" + time + " [" + message.level + ":" + message.level_desc + " ("+ message.tag + ")] " + message.message + " ]\r\n";
      stream.Write("[" + time + " [" + message.level + ":" + message.level_desc + " ("+ message.tag + ")] " + message.message + " ]\r\n");
    }

    protected override void onShutdown()
    {
      if (stream != null)
      {
        stream.Flush();
        stream.Close();
      }
    }

    /// <summary>
    /// Flag to append the text file.  If this flag is not set it will overwrite.
    /// </summary>
    /// <param name="flag"></param>
    public void setAppend(bool flag)
    {
      this.append = flag;
    }

    /// <summary>
    /// Get's append flag
    /// </summary>
    /// <returns></returns>
    public bool getAppend()
    {
      return append;
    }
  }

  /// <summary>
  /// Primary class responsbile for logging.  This class is a combination between a singleton
  /// and a regular class to allow for the developers to have more customability.
  /// 
  /// One thing worth mentioning, the only thing that this system will not allow developers to
  /// customize is the use of consecutive integers for log levels starting at 0.  Developers are
  /// free to use however many levels they want, define them however they want, however... that is the
  /// one limitation I am comfortable placing on them.
  /// </summary>
  public class Logger
  {
    private static Logger logger;
    protected static string[] logLevelDesc = null;

    protected LoggerEventHandler[][] leh;
    protected uint max = 0;
    protected uint levels = 0;
    protected LoggerEventHandler defaultHandler = null;
  
    /// <summary>
    /// With this constructor the developer is responsible for defining what
    /// they want the logger to do (in the defaultHandler).
    /// </summary>
    /// <param name="levels"></param>
    /// <param name="defaultHandler"></param>
    public Logger(uint levels, LoggerEventHandler defaultHandler)
    {
      init(levels, defaultHandler);
    }

    /// <summary>
    /// Opens up a standard to file logger with the specified number of log levels.
    /// </summary>
    /// <param name="levels"></param>
    /// <param name="filename"></param>
    public Logger(uint levels, string filename)
    {
      init(levels, new BasicFileLogEventHandler(filename));
    }

    /// <summary>
    /// User specifies only the number of log levels they require.  They are still obligated
    /// (assuming they want the logger to do something) to add a handler.
    /// </summary>
    /// <param name="levels"></param>
    public Logger(uint levels)
    {
      init(levels, null);
    }

    /// <summary>
    /// Takes the current object and places it as the internal singleton reference
    /// </summary>
    /// <returns></returns>
    public Logger promoteToStatic()
    {
      logger = this;
      return logger;
    }

    /// <summary>
    /// The singleton can get set in two ways:
    /// 
    /// 1.) The developer promotes their class to be the Logger object inside the singleton
    /// 2.) We decide for them what their class is going to look like
    /// </summary>
    /// <returns></returns>
    public static Logger singleton()
    {
      if (logger == null)
      {
        //use defaults
        string logFile = DateTime.Now.ToShortDateString().Replace(@"/",@"-").Replace(@"\",@"-") + ".log";
        logger = new Logger(6, logFile);
        logLevelDesc = new string[6];
        logLevelDesc[0] = "V_CRITICAL";
        logLevelDesc[1] = "V_ERROR";
        logLevelDesc[2] = "V_WARN";
        logLevelDesc[3] = "V_INFO";
        logLevelDesc[4] = "V_DEBUG";
        logLevelDesc[5] = "V_ALL";
      }
      
      return logger;
    }

    /// <summary>
    /// Effectively the constructor (so no code would have to be repeated).  Consequently
    /// if one were to try to put this block inside the constructor matching the signature and
    /// then referenced it with :this(levels, defaultHandler) in the other constructors MS
    /// has a bit of a fit.  Dunno...this works, don't really care.
    /// </summary>
    /// <param name="levels"></param>
    /// <param name="defaultHandler"></param>
    private void init(uint levels, LoggerEventHandler defaultHandler)
    {
      this.levels = levels;
      this.defaultHandler = defaultHandler;
      this.max = levels - 1;

      leh = new LoggerEventHandler[levels][];

      LoggerEventHandler[] handler = new LoggerEventHandler[1];

      handler[0] = defaultHandler;

      for(int i =0; i < levels; i++)
      {
        leh[i] = handler;
      }
    }


    /// <summary>
    /// This is the maximum level which will trigger logging.
    /// </summary>
    /// <param name="min"></param>
    public void setMaximumLogLevel(uint max)
    {
      this.max = max;
    }

    /// <summary>
    /// Retrieve maximum logging level (note: this is not the total number of levels, but rather
    /// the upper bound where an action will or won't take placed based on the log level)
    /// </summary>
    /// <returns></returns>
    public uint getMaximumLogLevel()
    {
      return max;
    }

    /// <summary>
    /// Retreives the default handler if one is set
    /// </summary>
    /// <returns></returns>
    public LoggerEventHandler getDefaultLoggerEventHandler()
    {
      return this.defaultHandler;
    }

    /// <summary>
    /// Adds a customized log handler to each log level
    /// </summary>
    /// <param name="handler"></param>
    public void addSpecialLoggerToAllLevels(LoggerEventHandler handler)
    {
      if (handler == null) return;

      for(int level = 0; level < this.levels; level++)
      {
        addSpecialLogger(level, handler);
      }
    }

    /// <summary>
    /// Adds a customized logger handler (e.g. log to file) to a specific level.
    /// Note: You can have (n log handlers assuming your system has the resources)
    /// </summary>
    /// <param name="level"></param>
    /// <param name="handler"></param>
    public void addSpecialLogger(int level, LoggerEventHandler handler)
    {
      if (level < levels)
      {
        if (leh[level] != null)
        {
          int size = leh[level].Length + 1;
          LoggerEventHandler[] temp = new LoggerEventHandler[size];

          for (int i = 0; i < leh[level].Length; i++)
          {
            temp[i] = leh[level][i];
          }

          temp[size-1] = handler;

          leh[level] = temp;
        }
        else
        {
          leh[level] = new LoggerEventHandler[1];
          leh[level][0] = handler;
        }
      }
    }

    /// <summary>
    /// Sugared method to add a simple file handler method
    /// </summary>
    /// <param name="level"></param>
    /// <param name="filename"></param>
    public void addSpecialLogger(int level, string filename)
    {
      addSpecialLogger(level, new BasicFileLogEventHandler(filename));
    }

    /// <summary>
    /// Invokes all appropriate log event handlers with the message
    /// </summary>
    /// <param name="level"></param>
    /// <param name="tag"></param>
    /// <param name="message"></param>
    public void log(int level, string tag, string message)
    {
      if ((level <= max) && (level < levels) && (leh[level] != null))
      {
        for(int i = 0; i < leh[level].Length; i++)
        {
          if (logLevelDesc == null)
          {
            if (leh[level][i] != null) leh[level][i].log(tag, level, "", message);
          }
          else
          {
            if (leh[level][i] != null) leh[level][i].log(tag, level, logLevelDesc[level], message);
          }
        }
      }
    }

    /// <summary>
    /// Invokes the shutdown method for all log handlers
    /// </summary>
    public void shutdown()
    {
      //This is not the most efficient, really I'd like to add a collection
      //in this class and loop through it (save some repeats), also allow for some other neet stuff.
      for (int level = 0; level < leh.Length; level++)
      {
        for(int i = 0; i < leh[level].Length; i++)
        {
          if (leh[level][i] != null) leh[level][i].shutdown();
        }
      }
    }
  }
}
www.java2v.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.