Only this pageAll pages
Powered by GitBook
1 of 59

2.x

Loading...

Intro

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Getting Started

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Configuration

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Usage

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Extending LogBox

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Introduction

LogBox Manual - Version 5.x

LogBox

LogBox is a standalone enterprise ColdFusion (CFML) logging library designed to give you flexibility, simplicity and power when logging or tracing is needed in your applications. LogBox is also part of the ColdBox Platform suite of services and libraries and allows you to easily build upon it's logging framework in order to meet any logging or reporting needs your applications has. LogBox surpasses ColdFusion's very basic cflog tag. LogBox allows you to create multiple destinations for your loggings and even configure multiple destinations or change them at runtime.

Almost every application needs logging and/or tracing capabilities and we have developed LogBox to satisfy these needs. Although you should take care not to over-use logging as it can slow down an application, LogBox offers you the capabilities to filter out or cancel logging noise a-la-carte. LogBox was inspired by the original logging capabilities in ColdBox and in the project.

Versioning

LogBox is maintained under the guidelines as much as possible.Releases will be numbered with the following format:

And constructed with the following guidelines:

  • Breaking backward compatibility bumps the major (and resets the minor and patch)

  • New additions without breaking backward compatibility bumps the minor (and resets the patch)

  • Bug fixes and misc changes bumps the patch

License

The ColdBox Platform, LogBox is open source and licensed under the License.

  • Copyright by Ortus Solutions, Corp

  • ColdBox is a registered trademark by Ortus Solutions, Corp

Info: The ColdBox Websites, Documentation, logo and content have a separate license and they are a separate entity.

Discussion & Help

The LogBox help and discussion group can be found here:

Reporting a Bug

We all make mistakes from time to time :) So why not let us know about it and help us out. We also love pull requests, so please star us and fork us:

  • By Email:

  • By Jira:

Professional Open Source

ColdBox is a professional open source software backed by offering services like:

  • Custom Development

  • Professional Support & Mentoring

  • Training

  • Server Tuning

Resources

  • Official Site:

  • Source Code:

  • Bug Tracker:

  • Twitter:

HONOR GOES TO GOD ABOVE ALL

Because of His grace, this project exists. If you don't like this, then don't read it, its not for you.

"Therefore being justified by **faith**, we have peace with God through our Lord Jesus Christ: By whom also we have access by **faith** into this **grace** wherein we stand, and rejoice in hope of the glory of God." Romans 5:5

 __        ______     _______ .______     ______   ___   ___ 
|  |      /  __  \   /  _____||   _  \   /  __  \  \  \ /  / 
|  |     |  |  |  | |  |  __  |  |_)  | |  |  |  |  \  V  /  
|  |     |  |  |  | |  | |_ | |   _  <  |  |  |  |   >   <   
|  `----.|  `--'  | |  |__| | |  |_)  | |  `--'  |  /  .  \  
|_______| \______/   \______| |______/   \______/  /__/ \__\

What's New With 5.3.0

Improvements

  • [LOGBOX-32] - Add test and fix for adding a LogBox category after the fact

Security Hardening

  • Code Reviews

  • Much More

  • Facebook: https://www.facebook.com/coldboxplatform

  • Google+: https://www.google.com/+ColdboxOrg

  • Vimeo Channel: http://vimeo.com/channels/coldbox

  • Log4j
    Semantic Versioning
    Apache 2
    https://groups.google.com/forum/#!forum/coldbox
    https://github.com/coldbox/coldbox-platform
    [email protected]
    https://ortussolutions.atlassian.net/browse/LOGBOX
    Ortus Solutions, Corp
    http://www.coldbox.org
    https://github.com/coldbox/coldbox-platform
    https://ortussolutions.atlassian.net/browse/LOGBOX
    @coldbox
    Ortus Solutions, Corp
    <major>.<minor>.<patch>

    What's New With 2.1.0

    This release is part of the ColdBox 4.2.0 update and contains the following updates:

    Improvement

    • [LOGBOX-19] - Update the documentation URL in `box.json`

    • [] - AbstractAppender.cfc function "CanLog" fails due to text/int comparison

    Installation

    LogBox can be downloaded as a standalone framework or it is included with the latest ColdBox Platform release. The main difference between both versions is the instantiation and usage namespace, the rest is the same.

    The best way to install LogBox is using CommandBox CLI and package manager.

    Adding Categories to Specific Loggin Levels

    The methods shown below are used to add categories to specific severity levels only. Each method can receive 1 to * category arguments.

    • public void debug()

    • public void info()

    What's New With 5.4.0

    Improvements

    There are two major improvements we did with LogBox in this release:

    1) The file locking operations on file appenders have been streamlined to avoid high i/o operations.

    2) The console appender uses an asynchronous streaming technique which makes it extremely efficient and fast.

    LogBox Refcard

    Our LogBox RefCard will get you up and running in no time

    Configuring LogBox

    No matter what configuration you decide to use, you will always have to instantiate LogBox with a LogBoxConfig object: logbox.system.logging.config.LogBoxConfig. However you have the option of either talking directly to this CFC or creating a more portable configuration. This portable configuration we denote as a simple data CFC that contains the LogBox configuration data using what we call our LogBox DSL (Domain Specific Language).

    The cool thing about this LogBox DSL is that it is exactly the same whether you are using LogBox in ColdBox applications or in any other framework or non-framework ColdFusion application. So you can configure LogBox by:

    1. Creating a portable data CFC using the LogBox DSL or

    LogBox from the ColdBox Proxy

    The ColdBox Proxy (coldbox.system.remote.ColdboxProxy) object also has three utility methods you can use to talk to LogBox from any remote proxy you create:

    • getLogBox() : Get a reference to the LogBox instance.

    • getRootLogger() : Get a reference to the root logger.

    Release Notes

    New Feature

    • [LOGBOX-34] - Console appender completely rewritten to support asynchronous streaming

    Improvement

    • [LOGBOX-33] - Improve file exists usage on file appenders to avoid i/o operations

    Creating the LogBoxConfig object and interacting with its methods

    getLogger(category:any) : Get a named logger instance or reference.

    LOGBOX-21

    ConsoleAppender

    Property

    The ConsoleAppender takes no additional properties.

    This appender logs using Java's System.out.println function. Logs will appear in the directory specificied in your CF or Lucee Administrator.

    Security Levels

    Need For Logging

    Most applications require logging and tracing capabilities. One can usually use ColdFusion's standard cflog or cftrace tags but you can reach a limitation very fast.

    • What if you needed to log only certain severity levels for a particular CFC or piece of code?

    • What if you needed that severity to advise you via SMS or Twitter (yes Twitter)?

    • What if you wanted to turn it off easily or reconfigure your logging levels?

    You would have to build all of these advising and logging capabilities yourself. Also, inserting log statements is tedious, time consuming and frequently pollutes your real code. You can understand the pain and complexity required to properly deal with these situations. These reasons (and more) are why the ColdBox team has invested in building LogBox.

    How Does LogBox Work?

    LogBox has four main components:

    1. LogBox

    2. Logger

    3. Appenders

    4. Layouts.

    These four (4) components work in unison to deliver the logging and tracing of messages and to control how they are logged. You will mostly interact with the Logger component as it will send your statements to the Appenders you have configured. Users can extend LogBox and build their own appenders and layouts.

    TraceAppender

    This appender directs messages via the cftrace tag. It has no configuration properties.

    Benefits of using LogBox in a ColdBox application

    Just by building a ColdBox application, you get several key benefits when dealing with LogBox.

    • First of all, the configuration, creation and instantiation is ALL done for you.

    • You can configure LogBox on a per-environment criteria as the ColdBox per-environment routines can use the LogBox configuration elements in its definitions.

    • Every handler and interceptor already has a reference to the LogBox instance as a property called: logBox. So you have immediate access to it.

    • Every handler and interceptor already has a configured logger instance as a property called: log. So you have immediate access to it.

    • You can configure the logging of ColdBox on a per package level.

    • You get the power of ColdBox MVC.

    Where is LogBox stored in a ColdBox app?

    The LogBox instance is stored in the ColdBox main controller object (coldbox.system.web.Controller) and you can retrieve it like so from any handler, or interceptor.

    logBox = getController().getLogBox();
    // or 
    logBox

    Registering Appenders at Runtime

    You can programmatically register appenders at runtime by using the registerAppender() function exposed in the LogBox object. Here is the function API:

    registerAppender(name, class, [properties={},] [layout="",] [levelMin=0,] [levelMax=4]);

    Info Please note that registering dynamic appenders at runtime is tricky as some objecs might already have references to some appenders. We recommend registering appenders at configuration load or when the application starts up.

    Creating a Custom Layout

    You can easily create a custom layout object by creating a cfc that extends our abstract layout object: logbox.system.logging.Layout and implementing a format() method. Below you can see the method signature:

    <---  format --->
    <cffunction name="format" output="false" access="public" returntype="string" hint="Format a logging event message into your own format">
        <cfargument name="logEvent" type="logbox.system.logging.LogEvent"   required="true"   hint="The logging event to use to create a message.">
    </cffunction>

    All you need to do is inspect the logging event and create your very own message and then return it back. That's it! You thought there was more?

    System Requirements
    • ColdFusion 11+

    • Lucee 4.5+

    Manual Installation

    If you are using LogBox within a ColdBox application context, then LogBox is part of the platform. Just install ColdBox normally. If you are using LogBox standalone, just drop LogBox in your application root or create a mapping called logbox that points to the installation folder. If you can run the following snippet, then LogBox is installed correctly:

    CommandBox Installation

    You can leverage CommandBox to install the standalone version of LogBox

    Namespaces

    Standalone

    logbox.system.logging

    ColdBox

    coldbox.system.logging

    Info In this book we will be using the standalone namespace for all examples.

    Useful Resources

    • LogBox Docs Source

    • LogBox Planning Board

    • Log4J

    • LogStash

    Download LogBox Standalone
    API Docs
    public void warn()
  • public void error()

  • public void fatal()

  • public void off()

  • config.debug( "com.model.myclass", "coldbox.system.controller" );
    config.info( "com.model.otherclass", "coldbox.system.whatever" );
    config.fatal( "com.model.otherclass", "coldbox.system.whatever" );
    config.error( "com.model.otherclass", "coldbox.system.whatever" );
    config.off( "com.model.otherclass", "coldbox.system.whatever" );
    logbox = new logbox.system.logging.LogBox();
    # Latest CommandBox
    box install logbox
    
    # Bleeding Edge
    box install logbox@be
    https://github.com/ColdBox/cbox-refcards/raw/master/LogBox/LogBox-Refcard.pdf

    What's New With 5.0.0

    This is a major release and a major rewrite of the entire framework to modern CFML, optimizations and script modes.

    We have also improved all appenders for performance and reliability. Especially, the file logging facilities have been drastically improved. The new file based appenders now rely on a queuing mechanism and listener watcher threads that stream content to the file repositories.

    Release Notes

    Improvements

    • [] - Allow chaining of logger programmatic config methods

    • [] - FileAppender IO improvements via LogListener Watchers

    • [] - Add extraInfo to the message before clean-up, to allow for correct csv exports

    LogBox

    LogBox is the core framework you need to instantiate in order to work with logging in your application. You have to instantiate it with a LogBoxConfig object that will hold your logging configurations. After the library is instantiated and configured you can ask from it a named Logger object so you can start logging or tracing messages.

    You have two ways to use LogBox:

    • Standalone Framework

    • Within a ColdBox application

    If you have downloaded LogBox as a standalone framework, then the initial namespace for the core is logbox.system. This allows you to use logbox as a standalone framework that is integrated into your proprietary application.

    The ColdBox Framework already has an instance of LogBox created for you in every application and it is stored in the main application controller: controller.getLogBox(). The namespace within the ColdBox framework is coldbox.system.

    Configuring The Root Logger

    Configuring a root logger is mandatory for LogBox. This is very easy and just a few arguments.

    public void root([numeric levelMin="-1",] [numeric levelMax="4",] string appenders)

    Parameters

    Name

    Description

    levelMin

    The default minimum log level for the root logger. (OPTIONAL. Defaults to 0 [FATAL].)

    Features at a Glance

    • LogBox can handle the inserting of logging and/or tracing statements in your application with a simple to use API while providing you the ability to manage logging behavior outside of your application code.

    • You can configure LogBox via a programmatic configuration file (cfc)

    • LogBox categorizes your logging and/or tracing statements according to user-defined categories that can be configured at runtime or pre-runtime. All of these categorizations can have their own logging level ranges (such as debug or info) and even their own destination points or what we refer to as LogBox Appenders (such as Console).

    • LogBox Appenders are the destination points you configure for your logging and/or tracing statements. LogBox also offers a basic extensible API so you can build and extend upon the Appender framework according to your unique logging or tracing needs. This gives you complete control and flexibility of how to expand LogBox without reinventing the wheel. Some appenders included in LogBox can log to the following destinations: File, Database, Sockets, Email, ColdFusion logging, System Console, and much more.

    • LogBox facilitates the creation of your very own customized message formats via Layouts. You can create a Layout component that can be configured in to ANY LogBox appender so it can spit out your very own customized messages.

    • LogBox can be instantiated as many times as you want and used as many times as you like in a single application. There are no restrictions upon its usage.

    • LogBox allows for category inheritance according to component and package conventions.

    CFAppender

    Property

    Type

    Required

    Default

    Description

    logType

    string(file or application)

    false

    file

    The type of cflog to use: file or application.

    fileName

    string

    This appender logs directly to the cflog tag by using a custom file or logging to the application logs.

    Layout

    The layout component defines the format of the message to store in an appender repository. Be default, each appender already has a pre-defined message format. However, if you do not like the format of the message you can easily change it by creating your own layout component and registering it with the appender. You can do this in the configuration object when you add appenders:

    Note: Not all appenders use a layout object (e.g. the DBAppender).

    So to create your very own layout object, you just need to extend the LogBox abstract layout object: logbox.system.logging.Layout and implement the format() method.

    Author

    Luis Fernando Majano Lainez

    Luis Majano is a Computer Engineer with over 15 years of software development and systems architecture experience. He was born in in the late 70’s, during a period of economical instability and civil war. He lived in El Salvador until 1995 and then moved to Miami, Florida where he completed his Bachelors of Science in Computer Engineering at . Luis resides in Rancho Cucamonga, California with his beautiful wife Veronica, baby girl Alexia and baby boy Lucas!

    He is the CEO of , a consulting firm specializing in web development, ColdFusion (CFML), Java development and all open source professional services under the ColdBox and ContentBox stack. He is the creator of ColdBox, ContentBox, WireBox, MockBox, LogBox and anything “BOX”, and contributes to many open source ColdFusion projects. He is also the Adobe ColdFusion user group manager for the

    Configuration Within ColdBox

    ColdBox allows for a programmatic approach via the ColdBox.cfc configuration object. So let's look at how the ColdBox loader looks at your configuration:

    1. Is there a logBox variable defined in the configuration?

      • False:

    LogBox in a ColdBox Application

    Every ColdBox application can use LogBox by default since the main engine already uses it. By default ANY ColdBox application will be configured with a LogBox instance with the following appenders:

    Also, the app will log on ANY severity by default up to INFO for the root logger and the ColdBox package. You can customize this default behavior by creating or modifying the LogBox element in your ColdBox configuration file and follow the same configuration approach as any normal LogBox configuration file.

    $toString() and ExtraInfo Argument

    When using any of the logging methods like info(), debug(), warn(), etc, they all take two arguments:

    1. The message to log

    2. An extraInfoargument which can be anything you like.

    Logger

    Logbox

    Instance Members

    Every Appender has access to the following public variables:

    Instance Members

    Every layout has access to the following public variables:

    Does a LogBox.cfc exist in the application's config folder?

    • True: Use that CFC by convention to configure LogBox

    • False: Continue to next point

  • Configure LogBox with default framework settings (coldbox.system.web.config.LogBox)

  • True:

    • Have you defined a configFile key?

      • True: Then use that value to pass into the configuration object so it can load LogBox using that configuration file (CFC)

      • False: The configuration data is going to be defined inline here so process it

  • So the configuration DSL is exactly the same as you have seen in before with the only distinction that you can add a configFile key that can point to an external configuration file (CFC).

    false

    Appender's name

    The name of the file to log to if using file as the logType. If not set, it will use the appender's name

    Property

    Description

    this.logLevels

    A reference to the logbox.system.logging.LogLevels class.

    Property

    Description

    this.logLevels

    A reference to the coldbox.system.logging.LogLevels class.

    this.LINE_SEP

    A line separator equal to chr(13) & chr(10).

    [LOGBOX-28] - Console Appender sent error messages to error out
    LOGBOX-16
    LOGBOX-20
    LOGBOX-26
    Logger Category Inheritance
    Security Levels
    Dynamic Appenders
    // Simple Config
    config = new logbox.system.logging.config.LogBoxConfig();
    // Create logbox instance
    logBox = new logbox.system.logging.LogBox( config );

    levelMax

    The default maximum log level for the root logger. (OPTIONAL. Defaults to 4 [DEBUG].)

    appenders

    A list of appenders to configure the root logger with. Use * to add all registered appenders

    config.root( appenders="*" );
    config.root( levelMax="WARN", appenders="console,files" );
    config.root( levelMin="INFO", levelMax="DEBUG", appenders="*" );
    //add a FileAppender with my own formatting
    props = { filePath='/logs', fileName='Test' };
    config.appender(
        name='Fileapp',
        class="logbox.system.logging.appenders.FileAppender",
        properties=props,
        layout="model.logging.MyFileLayout"
    );
    component{
    
        function configure(){
            logBox = {};
    
            // Define Appenders
            logBox.appenders = {
                console = { class="coldbox.system.logging.appenders.DummyAppender" }
            };
    
            // Root Logger
            logBox.root = {
                levelmax="OFF",
                levelMin="OFF",
                appenders="*"
            };
        }
    
    }

    Dealing With Custom Layouts

    In order for an appender to deal with custom layouts, you must use the layout methods when preparing to log your messages. Below is a simple example from the console appender of how to do this:

    if( hasCustomLayout() ){
      entry = getCustomLayout().format(loge);
    }
    else{
      entry = "#severityToString(loge.getseverity())# #loge.getCategory()# #loge.getmessage()# ExtraInfo: #loge.getextraInfoAsString()#";
    }
    
    // Log message to system.out
    instance.out.println(entry);

    As you can see, all you need to do is have an if statement that checks whether the appender has a custom layout or not and then assign the return of the layout as your message to log.

    Can Methods For Performance

    We recommend using the available can{severity}() methods to determine if we can log at a specific log level before actually writing the logging method line. This is done as best practice in order to avoid processing of messages that will never be logged anyways. So let's look at a very simple example of what NOT to do:

    log.debug( "This is my log message, some #dynamic# date is here", dataCFC );

    This will call the logger's debug() method, execute the lines of code and then the logger determines if it can be logged or not. This is ok, but we all love performance and best practice, so we encourage you to do the following:

    if( log.canDebug() ){
        log.debug( "This is my log message, some #dynamic# date is here", dataCFC );
    }

    This way, the logger determines if it can send debug log messages, and only IF IT CAN does it. This is faster and cleaner, but you will type more. Sorry!

    This extrainfo argument can be a simple value, a CFC, a complex object and pretty much anything you like. The appenders get this extraInfo argument and process it into their appropriate destinations by serializing its value. This is done by using the following algorithm:
    1. If it is a simple value, then just use it.

    2. If it is an object then check if the object has a method called $toString(). If the method exists, then call $toString() and use its return value.

    3. If it is an object with no $toString() method, then marshall its representation into XML format.

    4. If it is a complex variable like a struct, query, array, etc, then marshall it into JSON format.

    As you can see from the algorithm above, you can use the extraInfo argument to your benefit to save serialized representations of data to the appenders and then retrieve or re-inflate them later. The $toString() convention is great because you have complete control on how a CFC will serialize to its string representation. Let's see an example on a simple CFC:

    So when this object is sent to a logger's method, it will detect it is an object and the $toString() function exists and call it for serialization.

    // User.cfc
    component{
    
        function $toString(){
            // return my representation as a comma list of values of my properties
            return "#getName()#,#getAge()#,#getEmail()#";
        }
    }
    user = userService.getUser( rc.id );
    
    // need to log it.
    if( log.canDebug() ){
        log.debug( "User just got logged in right now!", user );
    }
    . You can read his blog at

    Luis has a passion for Jesus, tennis, golf, volleyball and anything electronic. Random Author Facts:

    • He played volleyball in the Salvadorean National Team at the tender age of 17

    • The Lord of the Rings and The Hobbit is something he reads every 5 years. (Geek!)

    • His first ever computer was a Texas Instrument TI-86 that his parents gave him in 1986. After some time digesting his very first BASIC book, he had written his own tic-tac-toe game at the age of 9. (Extra geek!)

    • He has a geek love for circuits, microcontrollers and overall embedded systems.

    • He has of late (during old age) become a fan of running and bike riding with his family.

    Keep Jesus number one in your life and in your heart. I did and it changed my life from desolation, defeat and failure to an abundant life full of love, thankfulness, joy and overwhelming peace. As this world breathes failure and fear upon any life, Jesus brings power, love and a sound mind to everybody!

    “Trust in the LORD with all your heart, and do not lean on your own understanding.” Proverbs 3:5

    Contributors

    Jorge Emilio Reyes Bendeck

    Jorge is an Industrial and Systems Engineer born in El Salvador. After finishing his Bachelor studies at the Monterrey Institute of Technology and Higher Education ITESM, Mexico, he went back to his home country where he worked as the COO of Industrias Bendek S.A.. In 2012 he left El Salvador and moved to Switzerland in persuit of the love of his life. He married her and today he resides in Basel with his lovely wife Marta and their daughter Sofía.

    Jorge started working as project manager and business developer at Ortus Solutions, Corp. in 2013, . At Ortus he fell in love with software development and now enjoys taking part on software development projects and software documentation! He is a fellow Cristian who loves to play the guitar, worship and rejoice in the Lord!

    Therefore, if anyone is in Christ, the new creation has come: The old has gone, the new is here! 2 Corinthians 5:17

    San Salvador, El Salvador
    Florida International University
    Ortus Solutions
    Inland Empire
    www.luismajano.com

    Adding Appenders

    The first thing you need to do in your config object is add appenders. Each appender is added via the appender() method.

    public void appender( string name, string class, [struct properties={},] [string layout,] [levelMin=0,] [levelMax=4] )

    Parameters:

    Name

    Description

    name

    Examples

    DBAppender

    Property

    Type

    Required

    Default

    Description

    dsn

    string

    true

    ---

    The dsn to use for logging

    table

    string

    The columns needed or created in the table are

    • id : UUID

    • severity : string

    • category : string

    If you are building a column mapper, the map must have the above keys in it that match to your own table columns.

    FileAppender

    Property

    Type

    Required

    Default

    Description

    filePath

    string

    true

    ---

    The location of where to store the log file

    filename

    string

    Info Please remember to set the autoExpand property to FALSE if you will be using an absolute file path location.

    What's New With 2.0.0

    Introduction

    LogBox 2.0.0 is a major release, mostly aligned to support our ColdBox 4 release.

    Release Notes

    You can find the release version information here:

    Bug

    • [] - Truncating of category data to avoid error on insertion on DB Appender

    Improvement

    • [] - deprecate logbox xml support

    New Feature

    • [] - Ability to asynchronize any logger via new 'async' property

    Adding Categories Granularly

    You can also add categories with very granular information using the category() method. This method will allow you to add a category definition with a range of severity levels and even a list of which appenders to respond to.

    public void category(
        string name,
        [numeric levelMin='0',]
        [numeric levelMax='4',]
        [string appenders='*']
    )

    Parameters:

    Name

    Description

    name

    Example

    ScopeAppender

    Property

    Type

    Required

    Default

    Description

    scope

    string

    false

    request

    The scope to persist to, any valid CF scope.

    key

    string

    The LogBox Injection DSL

    WireBox DI and Injection can talk to LogBox. This way you can easily use our dependency injection DSL for LogBox related objects:

    Type

    Description

    logbox

    Get a reference to the application's LogBox instance

    logbox:root

    Get a reference to the root logger

    logbox:logger:category

    Get a reference to a named logger by its category name

    logbox:logger:{this}

    Below you can see the most common usage of this dependency DSL:

    EmailAppender

    Dynamic Appenders

    Each logger object has several methods that you can use in order to interact with the logger's appenders. You can add, remove, clear or list the appenders on a specific logger instance. Below are the methods you can use in the logger class to interact with appenders:

    SocketAppender

    logdate : timestamp

  • appendername : string

  • message : string

  • extrainfo : string

  • true

    ---

    The table name to use for logging

    columnMap

    struct

    false

    ---

    A column map for aliasing columns. (Optional)

    autocreate

    boolean

    false

    false

    if true, then we will create the table. Defaults to false (Optional)

    false

    Name of the Appender

    The name of the file, if not defined, then it will use the name of this appender. Do not append an extension to it. We will append a .log to it

    fileEncoding

    string

    false

    utf-8

    The file encoding to use, by default we use UTF-8

    autoExpand

    boolean

    false

    true

    Whether to expand the file path or not. Defaults to true

    false

    appender's name

    The key to use in the scope

    limit

    numeric

    false

    0

    a limit to the amount of logs to rotate. Defaults to 0, unlimited (optional)

    https://ortussolutions.atlassian.net/browse/LOGBOX/fixforversion/12302
    LOGBOX-14
    LOGBOX-13
    LOGBOX-15

    ---

    The from email address

    to

    string

    true

    ---

    The to email(s)

    cc

    string

    false

    empty

    The cc email(s)

    bcc

    string

    false

    empty

    The bcc email(s)

    mailserver

    string

    false

    empty

    The optional mail server

    mailusername

    string

    false

    empty

    The optional mail username

    mailpassword

    string

    false

    empty

    The optional mail password

    mailport

    int

    false

    25

    The optional mail port

    useTLS

    boolean

    false

    false

    Use the Transport level security setting in the cfmail tag.

    useSSL

    boolean

    false

    false

    Use SSL or not

    Property

    Type

    Required

    Default

    Description

    subject

    string

    true

    ---

    Get's pre-pended with the severity and category field.

    from

    string

    true

    ---

    The port to connect to

    timeout

    numeric

    false

    5

    the timeout in seconds. defaults to 5 seconds

    persistConnection

    boolean

    false

    true

    Whether to persist the connection or create a new one every log time. Defaults to true

    Property

    Type

    Requierd

    Default

    Description

    host

    string

    true

    The host to connect to

    port

    string

    true

    A unique name for the appender to register. Only unique names can be registered per instance.

    class

    The appender's class to register. We will create, init it and register it for you.

    properties

    The structure of properties to configure this appender with. (OPTIONAL)

    layout

    The layout class path to use in this appender for custom message rendering. (OPTIONAL)

    levelMin

    The numerical or English word of the minimal logging level (OPTIONAL, defaults to 0 [FATAL].)

    levelMax

    The numerical or English word of the maximum logging level (OPTIONAL, defaults to 4 [DEBUG].)

    A unique name for the category to register. Only unique names can be registered per instance.

    levelMin

    The default min log level for this category. (OPTIONAL. Defaults to 0 [FATAL].)

    levelMax

    The max default log level for this category. (OPTIONAL. Defaults to 4 [DEBUG].)

    appenders

    A list of appender names to configure this category with else it will use all the appenders in the root logger. You can also use * to add all registered apenders. (OPTIONAL)

    //log all email service messages to the MyLogFileAppender and the Console.
    config.category( name="org.model.EmailService", appenders="MyLogFileAppender,Console" );

    Get a reference to a named logger according to the current class path of the injected target

    //  LogBox wired in
    property name="logBox" inject="logbox";
    
    //  Root Logger
    property name="logger" type="logbox:root";
    
    //  Named Category
    property name="logger" type="logbox:logger:com.api.model";
    
    //  Category eq to ClassPath
    property name="logger" type="logbox:logger:{this}";

    Appender

    Return a named appender if it is registered in the logger

    appenderExists(name)

    Boolean

    Checks if a named appender exists in the logger

    addAppender(Appender)

    void

    Register an appender with the logger at runtime

    removeAppender(name)

    Boolean

    Will un-register an appender from this logger

    removeAllAppenders()

    void

    Will try to un-register all appenders from this logger

    So you can easily add/remove/check the appenders on any logger at any time.

    Method

    Return Type

    Description

    hasAppenders()

    Boolean

    Checks if the logger has any appenders attached to it

    getAppenders()

    Struct

    Returns the map of registered appenders

    getAppender(name)

    config.appender(
        name="CFConsole",
        class="coldbox.system.logging.appenders.ConsoleAppender"
    );
    
    config.appender(
        name="MyCF",
        class="coldbox.system.logging.appenders.CFAppender"
    );
    
    config.appender(
        name="SocketBaby",
        class="coldbox.system.logging.appenders.SocketAppender",
        properties={ host="localhost", port="444", timeout="3", persistConnection=false }
    );
    
    config.appender(
        name='Fileapp',
        class="coldbox.system.logging.appenders.FileAppender",
        properties={ filePath="/logs", fileName="Test" },
        layout="model.logging.MyFileLayout"
    );
    //Add your own appender at runtime
    jms = createObject( "component", "com.appender.JMSAppender" ).init( "JMSAppender", properties );
    logger.addAppender( jms );
    
    //log a message to all appenders and to my jms appender:
    logger.fatal( "I FAILED MAN!" );
    
    //remove it
    logger.removeAppender( "JMSAppender" );

    Using a Logger Object

    Once you retrieve a logger object from LogBox, you are ready to start sending messages. We already covered how to dynamically add/remove/list/check appenders from a logger, so let's look at the other methods we have available:

    Instance Members

    Every logger has access to the following public variables:

    Property

    Description

    Utility Methods

    Logging Methods

    As you can probably tell, all logging methods take in a message string an a second argument called extraInfo. This extraInfo argument can be anything from a string, a structure, a query or whatever. This way you can send in a complex structure that the appenders will serialize into message form or log into the appropriate channel. Thus, extraInfo can be very handy when you are building your own custom appenders.

    I hope that by now you understand the basics of loggers and how easy it is to use them.

    RollingFileAppender

    Property

    Type

    Required

    Default

    Description

    filePath

    string

    true

    ---

    The location of where to store the log file

    filename

    string

    Info Please remember to set the autoExpand property to FALSE if you will be using an absolute file path location.

    Helper Methods

    The abstract appender also has various cool methods that you can use when building appenders:

    Properties Methods

    Method

    Description

    struct getProperties()

    Get the entire properties struct.

    Utility Methods

    Layout Methods

    Using LogBox

    Once you have created and configured the LogBox library, you can interact with it in order to get logger objects. The main methods you will use to interact with LogBox are the following, but I recommend you look at the CFC api (http://www.coldbox.org/api) in order to get a listing of all available methods.

    Method

    Description

    LogBoxConfig getConfig()

    Get the config object registered

    Logger getLogger(any category)

    Get a named logger object using a category string or the object you will log from

    Logger getRootLogger()

    Get a reference to the root logger

    The two most important methods are getRootLogger() & getLogger(), which you will use to get the root or named logger objects.

    Caution: When you ask for a named category logger and LogBox cannot find its definition, it will create a logger that will inherit its logging levels and appenders from the root logger.

    Appender

    An appender is an object that LogBox uses to log statements to a destination repository. All appenders act as destinations that can include: databases, JMS, files, consoles, sockets, etc. The appender has the responsibility of taking the logged message and persisting the message or sending the message to an external service. LogBox comes bundled with the following appenders that can be found in the package coldbox.system.logging.appenders:

    Introduction

    LogBox Manual - Version 5.0.0

    LogBox is a standalone enterprise ColdFusion (CFML) logging library designed to give you flexibility, simplicity and power when logging or tracing is needed in your applications. LogBox is also part of the ColdBox Platform suite of services and libraries and allows you to easily build upon it's logging framework in order to meet any logging or reporting needs your applications has. LogBox surpasses ColdFusion's very basic cflog tag. LogBox allows you to create multiple destinations for your loggings and even configure multiple destinations or change them at runtime.

    Almost every application needs logging and/or tracing capabilities and we have developed LogBox to satisfy these needs. Although you should take care not to over-use logging as it can slow down an application, LogBox offers you the capabilities to filter out or cancel logging noise a-la-carte. LogBox was inspired by the original logging capabilities in ColdBox and in the

    false

    Name of the Appender

    The name of the file, if not defined, then it will use the name of this appender. Do not append an extension to it. We will append a .log to it

    fileEncoding

    string

    false

    utf-8

    The file encoding to use, by default we use UTF-8

    autoExpand

    boolean

    false

    true

    Whether to expand the file path or not. Defaults to true

    fileMaxSize

    int

    false

    2000 (2MB)

    The max file size for log files. Defaults to 2000 (2 MB)

    fileMaxArchives

    int

    false

    2

    The max number of archives to keep. Defaults to 2

    void setProperties(struct properties)

    Override all properties with a new properties struct.

    any getProperty(string property)

    Get a property.

    void setProperty(string property, any value)

    Set a property.

    boolean propertyExists(string property)

    Checks if a property exists.

    Method

    Description

    boolean isInitialized()

    Returns true if the appender has been initialized.

    string getName()

    Get the name of the appender

    string getHash()

    Get the appender's unique hash id

    string severityToString(numeric severity)

    Transforms a severity integer to it's human readable form

    Method

    Description

    any getCustomLayout()

    Get the custom layout object if defined.

    boolean hasCustomLayout()

    Checks if the custom layout object is defined in this appender.

    string getVersion()

    Get the current version of LogBox

    string getCurrentAppenders()

    Get a list of currently registered appenders

    string getCurrentLoggers()

    Get a list of currently instantiated loggers

    void configure( LogBoxConfig config )

    Dynamically re-configure the LogBox library

    Set the minimum severity level.

    numeric getLevelMax()

    Get the maximum severity level.

    void setLevelMax(level)

    Set the maximum severity level.

    Log any kind of message.

    this.logLevels

    A reference to the logbox.system.logging.LogLevels class.

    Method

    Description

    boolean canLog(numeric level)

    Checks if this logger can log a certain type of severity. There is also a can{severity}() method for each severity level.

    boolean can{severity}()

    Checks if this logger can log a certain type of severity.

    void setCategory(category)

    Set the category name.

    Logger getRootLogger()

    Get the root logger.

    numeric getLevelMin()

    Get the minimum severity level.

    Method

    Description

    fatal(string message, [any extraInfo=""])

    Log a fatal message.

    error(string message, [any extraInfo=""])

    Log an error message.

    warn(string message, [any extraInfo=""])

    Log a warning message.

    info(string message, [any extraInfo=""])

    Log an information message.

    debug(string message, [any extraInfo=""])

    Log a debug message.

    void setLevelMin(level)

    logMessage(string message, numeric severity, [any extraInfo=""])

    DummyAppender

    An endless void of nothingness

    EmailAppender

    Will deliver messages to any email address.

    FileAppender

    Will deliver messages a file.

    RollingFileAppender

    A file appender that can do file rotation and archiving.

    ScopeAppender

    Will deliver messages to any ColdFusion variable scope.

    SocketAppender

    Will connect to any server socket and deliver messages.

    TracerAppender

    Will deliver messages to the ColdFusion tag cftrace.

    Asynchronous Appenders

    You may wish your logs to be asynchronous. You can do so by passing an async property in your configuration. ( This happens at the logger level and is available to all appenders out of the box, even ones that you create yourself!

    Configuration

    You can configure LogBox to use one or all of these appenders at any point in time. You can even register as many instances of any appender by defining a unique name for each. Here are examples of how one can configure appenders programmatically or via the simple configuration CFC:

    Programmatic Approach

    Configuration CFC approach

    Another feature of a LogBox appender is that you can extend them or create new ones simply by leveraging the LogBox API. To customize LogBox appenders for your own unique needs you would simply extend the core appender class: coldbox.system.logging.AbstractAppender and implementing the init() and logMessage() methods. Extending LogBox will be reviewed in greater detail over the next few sections.

    Appender

    Description

    CFAppender

    Will deliver messages to the coldfusion logs.

    ConsoleAppender

    Will deliver messages to the server's console via system.out

    DBAppender

    Will deliver messages to a database table. It can auto create the table for you.

    // setting some messages
    myLogger = logBox.getLogger( this ); // "com.model.dao"
    
    myLogger.info( "I just created my first logger" );
    
    try{
        data = dao.getDBData();
    }
    catch( any e ){
        myLogger.error(
            "Something really died on my dbdata method: #e.message# #e.detail#",
            e.tagContext
        );
    }
    // Any appender can be asynchronous!
    dbDebugger = {
        class="logbox.system.logging.appenders.DBAppender",
        properties={
            dsn="blog",
            table="logs",
            autocreate=true,
            textDBType="NCLOB",
            async=true
        }
    }
    // Adding appenders
    props = {
        filePath=expandPath( "/logbox/testing/cases/logging/tmp" ),
        autoExpand=false,
        fileMaxArchives=1,
        fileMaxSize=3000,
        async=true
    };
    
    config.appender(
        name="MyAsyncFile",
        class="logbox.system.logging.appenders.RollingFileAppender",
        properties=props
    );
    
    // Socket
    props = { host="localhost", port="444", timeout="3", persistConnection=false };
    config.appender(
        name="SocketAppender",
        class="logbox.system.logging.appenders.SocketAppender",
        properties=props
    );
    function configure(){
    
        logBox = {
            // Register Appenders
            appenders = {
                MyAsyncFile = {
                    class="logbox.system.logging.appenders.RollingFileAppender",
                    properties={
                        filePath=expandPath( "/logbox/testing/cases/logging/tmp" ),
                        autoExpand=false,
                        fileMaxArchives=1,
                        fileMaxSize=3000,
                        async=true
                    }
                },
    
                SocketAppender = {
                    class="logbox.system.logging.appenders.SocketAppender",
                    properties={
                        host="localhost",
                        port="444",
                        timeout="3",
                        persistConnection=false
                    }
                }
            }
        };
    
    }
    project.

    Versioning

    LogBox is maintained under the Semantic Versioning guidelines as much as possible.Releases will be numbered with the following format:

    And constructed with the following guidelines:

    • Breaking backward compatibility bumps the major (and resets the minor and patch)

    • New additions without breaking backward compatibility bumps the minor (and resets the patch)

    • Bug fixes and misc changes bumps the patch

    License

    The ColdBox Platform, LogBox is open source and licensed under the Apache 2 License.

    • Copyright by Ortus Solutions, Corp

    • ColdBox is a registered trademark by Ortus Solutions, Corp

    Info: The ColdBox Websites, Documentation, logo and content have a separate license and they are a separate entity.

    Discussion & Help

    The LogBox help and discussion group can be found here: https://groups.google.com/forum/#!forum/coldbox

    Reporting a Bug

    We all make mistakes from time to time :) So why not let us know about it and help us out. We also love pull requests, so please star us and fork us: https://github.com/coldbox/coldbox-platform

    • By Email: [email protected]

    • By Jira: https://ortussolutions.atlassian.net/browse/LOGBOX

    Professional Open Source

    Ortus Solutions, Corp

    ColdBox is a professional open source software backed by Ortus Solutions, Corp offering services like:

    • Custom Development

    • Professional Support & Mentoring

    • Training

    • Server Tuning

    • Security Hardening

    • Code Reviews

    Resources

    • Official Site: http://www.coldbox.org

    • Source Code: https://github.com/coldbox/coldbox-platform

    • Bug Tracker: https://ortussolutions.atlassian.net/browse/LOGBOX

    • Twitter: @coldbox

    • Facebook:

    • Google+:

    • Vimeo Channel:

    HONOR GOES TO GOD ABOVE ALL

    Because of His grace, this project exists. If you don't like this, then don't read it, its not for you.

    "Therefore being justified by **faith**, we have peace with God through our Lord Jesus Christ: By whom also we have access by **faith** into this **grace** wherein we stand, and rejoice in hope of the glory of God." Romans 5:5

    LogBox
    Log4j
     __        ______     _______ .______     ______   ___   ___ 
    |  |      /  __  \   /  _____||   _  \   /  __  \  \  \ /  / 
    |  |     |  |  |  | |  |  __  |  |_)  | |  |  |  |  \  V  /  
    |  |     |  |  |  | |  | |_ | |   _  <  |  |  |  |   >   <   
    |  `----.|  `--'  | |  |__| | |  |_)  | |  `--'  |  /  .  \  
    |_______| \______/   \______| |______/   \______/  /__/ \__\
    <major>.<minor>.<patch>
    Much More
    https://www.facebook.com/coldboxplatform
    https://www.google.com/+ColdboxOrg
    http://vimeo.com/channels/coldbox

    About This Book

    The source code for this book is hosted in GitHub: https://github.com/ortus-docs/logbox-docs. You can freely contribute to it and submit pull requests. The contents of this book is copyright by Ortus Solutions, Corp and cannot be altered or reproduced without author's consent. All content is provided "As-Is" and can be freely distributed.

    • The majority of code examples in this book are done in cfscript.

    • The majority of code generation and running of examples are done via CommandBox: The ColdFusion (CFML) CLI, Package Manager, REPL - https://www.ortussolutions.com/products/commandbox

    • All ColdFusion examples designed to run on the open soure Railo Platform or Adobe ColdFusion 9.0.2+

    External Trademarks & Copyrights

    Flash, Flex, ColdFusion, and Adobe are registered trademarks and copyrights of Adobe Systems, Inc. Railo is a trademark and copyright of Railo Technologies, GmbH.

    Notice of Liability

    The information in this book is distributed “as is”, without warranty. The author and Ortus Solutions, Corp shall not have any liability to any person or entity with respect to loss or damage caused or alleged to be caused directly or indirectly by the content of this training book, software and resources described in it.

    Contributing

    We highly encourage contribution to this book and our open source software. The source code for this book can be found in our where you can submit pull requests.

    Charitable Proceeds

    15% of the proceeds of this book will go to charity to support orphaned kids in El Salvador - . So please donate and purchase the printed version of this book, every book sold can help a child for almost 2 months.

    Shalom Children's Home

    Shalom Children’s Home () is one of the ministries that is dear to our hearts located in El Salvador. During the 12 year civil war that ended in 1990, many children were left orphaned or abandoned by parents who fled El Salvador. The Benners saw the need to help these children and received 13 children in 1982. Little by little, more children werecame on their own, churches and the government brought children to them for care, and the Shalom Children’s Home was founded.

    Shalom now cares for over 80 children in El Salvador, from newborns to 18 years old. They receive shelter, clothing, food, medical care, education and life skills training in a Christian environment. The home is supported by a child sponsorship program.

    We have personally supported Shalom for over 6 years now; it is a place of blessing for many children in El Salvador that either have no families or have been abandoned. This is good earth to seed and plant.

    LogBox DSL

    In order to create a simple data CFC, just create a CFC with one required method on it called configure where you will define the logging configuration data:

    Base Config

    Once you have this shell, you will create a logBox variable in the variables scope that must be a structure with the following keys:

    Appender Properties

    As we mentioned before, LogBox ships with over 10 different appenders for your logging and tracing needs. Some of them require configuration properties and some don't. We already discovered that when we configure an appender we can pass in a structure of properties much like how we configure ColdBox Interceptors. Each appender can implement as many properties as they see fit. We will digest all the included LogBox appenders and their configuration properties.

    Async Property

    All appenders have an async boolean property by default inherited from LogBox. This will tell LogBox to log asynchronously in a separate thread. By default all logging is done synchronously

    Description

    appenders

    A structure where you will define appenders

    root

    A structure where you will configure the root logger

    categories

    A structure where you can define granular categories (OPTIONAL)

    DEBUG

    An array that will hold all the category names to place under the DEBUG logging level (OPTIONAL)

    INFO

    An array that will hold all the category names to place under the INFO logging level (OPTIONAL)

    WARN

    An array that will hold all the category names to place under the WARN logging level (OPTIONAL)

    ERROR

    An array that will hold all the category names to place under the ERROR logging level (OPTIONAL)

    FATAL

    An array that will hold all the category names to place under the FATAL logging level (OPTIONAL)

    OFF

    An array that will hold all the category names to not log at all (OPTIONAL)

    Appenders

    To define an appender you must define a struct with a key value which is the internal name of the appender. Each appender name must be unique. You configure each appender with the following keys:

    Key

    Description

    class

    The class path of the appender

    properties

    The properties struct for the appender (OPTIONAL)

    layout

    The layout class path of the layout object to use (OPTIONAL)

    levelMin

    The numerical or English word of the minimal logging level (OPTIONAL, defaults to 0 [FATAL])

    levelMax

    The numerical or English word of the maximum logging level (OPTIONAL, defaults to 4 [DEBUG])

    Root Logger

    To configure the root logger use the following keys:

    Key

    Description

    levelMin

    The numerical or English word of the minimal logging level (OPTIONAL, defaults to 0 [FATAL])

    levelMax

    The numerical or English word of the maximum logging level (OPTIONAL, defaults to 4 [DEBUG])

    appenders

    A string list of the appenders to use for logging

    Categories

    To define categories you must define a struct with a key value which is the internal name of the category. Each category name must be unique. You configure each category with the following keys:

    Key

    Description

    levelMin

    The numerical or English word of the minimal logging level (OPTIONAL, defaults to 0 [FATAL])

    levelMax

    The numerical or English word of the maximum logging level (OPTIONAL, defaults to 4 [DEBUG])

    appenders

    A string list of the appenders to use for logging (OPTIONAL, defaults to *)

    As you might notice the name of the keys on all the structures match 100% to the programmatic methods you can also use to configure logBox. So when in doubt, refer back to the argument names.

    Example Configuration

    Instantiation

    Once you have defined the configuration data in this object you can now use the same LogBox Config object to either instantiate it for you or you can pass a reference of it by using the init() method of the LogBoxConfig object:

    • CFCConfig : The object instance that has the logbox configuration data

    • CFCConfigPath : The instantiation path of the object that has the logbox configuration data

    That's it! Using this DSL approach, your configurations are much more portable now and can even be shared in ANY framework, ColdBox or ColdFusion application. So now let's explore how to bypass this data CFC and use the LogBoxConfig object directly. It is important to understand these methods as they are called for you when you define your LogBox DSL data.

    Key

    **
    * A LogBox configuration data object
    */
    component{
    
        function configure(){
            logBox={
    
            };
        }
    }
    logBox = {
        // Appenders
        appenders = {
            appenderName = {
                class="class.to.appender",
                layout="class.to.layout",
                levelMin=0,
                levelMax=4,
                properties={
                    name  = value,
                    prop2 = value 2
                }
        },
        // Root Logger
        root = { levelMin="FATAL", levelMax="DEBUG", appenders="*" },
        // Granualr Categories
        categories={
            "coldbox.system" = { levelMin="FATAL", levelMax="INFO", appenders="*" },
            "model.security" = { levelMax="DEBUG", appenders="console" }
        }
        // Implicit categories
        debug = [ "coldbox.system.interceptors"  ],
        info  = [ "model.class", "model2.class2" ],
        warn  = [ "model.class", "model2.class2" ],
        error = [ "model.class", "model2.class2" ],
        fatal = [ "model.class", "model2.class2" ],
        off   = [ "model.class", "model2.class2" ]
    };
    init([CFCConfig,CFCConfigPath])
    // Using config path
    config = createObject( "component", "logbox.system.logging.config.LogBoxConfig" )
        .init( CFCConfigPath="my.path.LogBoxConfig" );
    logBox = createObject( "component", "logbox.system.logging.LogBox" ).init( config );
    
    // Using config object
    data = createObject( "component", "my.data.CFC" );
    config = createObject( "component", "logbox.system.logging.config.LogBoxConfig" ).init( data );
    logBox = createObject( "component", "logbox.system.logging.LogBox" ).init( config );
    GitHub repository
    http://www.harvesting.org/
    http://www.harvesting.org/
    Shalom Children's Home

    Logger Category Inheritance

    We have a convention for our category names where each category name uses dot-notation according to the components path. Using a class-path convention for our category names allows one to pseudo-inherit for logging levels and appenders! The following example should help clarify this concept.

    The overall premise is that when you request a logger with a category name LogBox will search for it's configuration. If LogBox does not find a configuration for the category name it will try to locate its closest ancestor for logging levels and appenders. If LogBox cannot find an ancestor the message will be logged using the root logger information. For example, let's say we define some appenders like this:

    And some categories like this:

    Then, let's say we request the following logger objects and logged some info:

    Information All example code snippets are using a getLogger( "categoryname" ) call instead of our preferred approach of getLogger( this ) because we want to showcase which category we are talking about. Please take this into consideration.

    Since we requested the category: coldbox.system.plugins.BeanFactory, LogBox tries to locate it, but it has not been defined, so it takes off the last item in the category name. Now it will search for a category of: coldbox.system.plugins via pseudo-inheritance. However, now coldbox.system.plugins has been found and it has been configured to only listen to error messages. Therefore, the coldbox.system.plugins.BeanFactory logger can ONLY log error messages according to it's inherited category. So the info() message will be ignored, and the error() message will be sent to both the FileAppender and the ConsoleAppender!

    The second logger is called coldbox.system.interceptors.SES, LogBox tries to match a category but fails, so it now searches for a logger called coldbox.system.interceptors. It still cannot find it so it continues up the package chain and finds the coldbox.system logger which has been set with a minimum of INFO level and ONLY the console appender. So the messages that get logged is the logger.info() and the logger.debug() message and they will be sent to the console appender.

    These examples should give you insight into category inheritance and the power they provide. You can easily turn toggle logging for entire packages with a single category definition. However, this is great only if you follow the dot notation conventions. Below is a sample generic chart sample:

    // Appenders
    config.appender(
      name="console",
      class="logbox.system.logging.appenders.ConsoleAppender"
    );
    config.appender(
      name="file",
      class="logbox.system.logging.appenders.FileAppender",
      properties={ filePath="/logs" }
    )
    // Explicit Categories
    config.category(
      name="coldbox.system",
      levelMin=config.logLevels.INFO,
      appenders="console"
    );
    
    // Implicit Categories
    config.error(
      "coldbox.system.plugins"
    );
    // same as
    config.cateogry(
      name="coldbox.system.plugins",
      levelMax="ERROR"
    );
    logger = logBox.getLogger( "coldbox.system.plugins.BeanFactory" );
    logger.info( "hello info" );
    logger.error( "wow and error occurred" );
    
    logger = logBox.getLogger( "coldbox.system.interceptors.SES" );
    logger.info( "hello info" );
    logger.debug( "a cool debug message" );

    ERROR

    *

    coldbox.system.interceptors.SES

    coldbox.system.interceptors.SES

    INFO-DEBUG from coldbox.system

    console from coldbox.system

    coldbox.system.plugins.BeanFactory

    NONE

    ERROR from coldbox.system.plugins

    *

    INFO from x.y

    Category

    Configured Levels

    Assigned Levels

    Appenders

    root

    FATAL-DEBUG

    FATAL-DEBUG

    console,file

    coldbox.system

    INFO-DEBUG

    INFO-DEBUG

    console

    coldbox.system.plugins

    Category

    Configured Levels

    Assigned Levels

    root

    FATAL-DEBUG

    FATAL-DEBUG

    x

    NONE

    FATAL-DEBUG from root

    x.y

    INFO

    INFO

    x.y.z

    ERROR

    NONE

    Creating Custom Appenders

    In order to create your own appenders, you will have to create a cfc that extends logbox.system.logging.AbstractAppender and implement the following methods:

    Method

    Description

    init()

    Your constructor. Make sure to call super.init( argumentCollection=arguments );

    logMessage()

    The method that is called when a message is received.

    onRegistration()

    An interceptor that fires when the appender gets created and initialized. It can be used for preparing the appender for operation.

    init()

    The signature of the init method is the following:

    As you can see each appender receives a name, a structure of properties, an optional layout class, and an optional levelMin and levelMax severity levels. The properties and layout are both optional, but you must call the super.init( argumentCollection = arguments ) method in order to have full ok operation on the appender. You can then do your own constructor as you see fit. Here is an example:

    logMessage()

    The signature of the logMessage method is the following:

    As you can see it is a very simple method that receives a LogBox logging event object. This object keeps track of the following properties with its appropriate getters and setters:

    • timestamp

    • category

    • message

    You can then use this logging event object to log to whatever destination you want. Here is a snippet from our scope appender:

    onRegistration() & onUnregistration()

    Finally, both the onRegistration and onUnregistration methods have to be void methods with no arguments.

    These are great for starting or stopping your appenders if they so need to. Here is a sample from our socket appender:

    severity
  • extraInfo

  • onUnRegistration()

    An interceptor that fires when the appender is removed from a logger.

    <---  Init --->
    <cffunction name="init" access="public" returntype="AbstractAppender" hint="Constructor called by a Concrete Appender" output="false" >
        <---  ************************************************************* --->
        <cfargument name="name"         type="string"  required="true" hint="The unique name for this appender."/>
        <cfargument name="properties"     type="struct"  required="false" default="#structnew()#" hint="A map of configuration properties for the appender"/>
        <cfargument name="layout"         type="string"  required="false" default="" hint="The layout class to use in this appender for custom message rendering."/>
        <cfargument name="levelMin"      type="numeric" required="false" default="0" hint="The default log level for this appender, by default it is 0. Optional. ex: LogBox.logLevels.WARN"/>
        <cfargument name="levelMax"      type="numeric" required="false" default="4" hint="The default log level for this appender, by default it is 5. Optional. ex: LogBox.logLevels.WARN"/>
        <---  ************************************************************* --->
    
    </cffunction>
    <---  Constructor --->
    <cffunction name="init" access="public" returntype="FileAppender" hint="Constructor" output="false">
        <---************************************************************** --->
        <cfargument name="name"         type="string"  required="true" hint="The unique name for this appender."/>
        <cfargument name="properties"     type="struct"  required="false" default="#structnew()#" hint="A map of configuration properties for the appender"/>
        <cfargument name="layout"     type="string"  required="true"  default="" hint="The layout class to use in this appender for custom message rendering."/>
        <cfargument name="levelMin"      type="numeric" required="false" default="0" hint="The default log level for this appender, by default it is 0. Optional. ex: LogBox.logLevels.WARN"/>
        <cfargument name="levelMax"      type="numeric" required="false" default="4" hint="The default log level for this appender, by default it is 5. Optional. ex: LogBox.logLevels.WARN"/>
            <---************************************************************** --->
        <cfscript>
            super.init(argumentCollection=arguments);
    
            // Setup Properties
            if( NOT propertyExists("filepath") ){
                $throw(message="Filepath property not defined",type="FileAppender.PropertyNotFound");
            }
            if( NOT propertyExists("autoExpand") ){
                setProperty("autoExpand",true);
            }
            if( NOT propertyExists("filename") ){
                setProperty("filename",getName());
            }
            if( NOT propertyExists("fileEncoding") ){
                setProperty("fileEncoding","UTF-8");
            }
    
            // Setup the log file full path
            instance.logFullpath = getProperty("filePath");
            // Clean ending slash
            if( right(instance.logFullpath,1) eq "/" OR right(instance.logFullPath,1) eq "\"){
                instance.logFullPath = left(instance.logFullpath, len(instance.logFullPath)-1);
            }
            instance.logFullPath = instance.logFullpath & "/" & getProperty("filename") & ".log";
    
            // Do we expand the path?
            if( getProperty("autoExpand") ){
                instance.logFullPath = expandPath(instance.logFullpath);
            }
    
            //lock information
            instance.lockName = getname() & "logOperation";
            instance.lockTimeout = 25;
    
            return this;
        </cfscript>
    </cffunction>
    <---  logMessage --->
    <cffunction name="logMessage" access="public" output="false" returntype="void">
        <cfargument name="logEvent" type="logbox.system.logging.LogEvent" required="true" hint="The logging event to log.">
    </cffunction>
    <---  Log Message --->
    <cffunction name="logMessage" access="public" output="true" returntype="void" hint="Write an entry into the appender.">
        <---************************************************************** --->
        <cfargument name="logEvent" type="logbox.system.logging.LogEvent" required="true" hint="The logging event"/>
        <---************************************************************** --->
        <cfscript>
            var logStack = "";
            var entry = structnew();
            var limit = getProperty('limit');
            var loge = arguments.logEvent;
    
            // Verify storage
            ensureStorage();
    
            // Check Limits
            logStack = getStorage();
    
            if( limit GT 0 and arrayLen(logStack) GTE limit ){
                // pop one out, the oldest
                arrayDeleteAt(logStack,1);
            }
    
            // Log Away
            entry.id = createUUID();
            entry.logDate = loge.getTimeStamp();
            entry.appenderName = getName();
            entry.severity = severityToString(loge.getseverity());
            entry.message = loge.getMessage();
            entry.extraInfo = loge.getextraInfo();
            entry.category = loge.getCategory();
    
            // Save Storage
            arrayAppend(logStack, entry);
            saveStorage(logStack);
        </cfscript>
    </cffunction>
    <cffunction name="onRegistration" access="public" hint="Runs after the appender has been created and registered. Implemented by Concrete appender" output="false" returntype="void">
    </cffunction>
    
    <cffunction name="onUnRegistration" access="public" hint="Runs before the appender is unregistered from LogBox. Implemented by Concrete appender" output="false" returntype="void">
    </cffunction>
    <---  onRegistration --->
    <cffunction name="onRegistration" output="false" access="public" returntype="void" hint="When registration occurs">
        <cfif getProperty("persistConnection")>
            <cfset openConnection()>
        </cfif>
    </cffunction>
    
    <---  onRegistration --->
    <cffunction name="onUnRegistration" output="false" access="public" returntype="void" hint="When Unregistration occurs">
        <cfif getProperty("persistConnection")>
            <cfset closeConnection()>
        </cfif>
    </cffunction>