Only this pageAll pages
Powered by GitBook
1 of 59

5.x

Loading...

Intro

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Getting Started

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Security Levels

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...

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.

Release Notes

New Feature

Improvement

[] - Console appender completely rewritten to support asynchronous streaming

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

LOGBOX-34
LOGBOX-33

Introduction

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

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.

Versioning

<major>.<minor>.<patch>

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

  • 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

Reporting a Bug

Professional Open Source

  • Custom Development

  • Professional Support & Mentoring

  • Training

  • Server Tuning

  • Security Hardening

  • Code Reviews

Resources

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

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.

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

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

The LogBox help and discussion group can be found here:

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:

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

Official Site:

Source Code:

Bug Tracker:

Twitter:

Facebook:

Google+:

Vimeo Channel:

Log4j
Semantic Versioning
Apache 2
https://groups.google.com/forum/#!forum/coldbox
https://github.com/coldbox/coldbox-platform
bugs@coldbox.org
https://ortussolutions.atlassian.net/browse/LOGBOX
Ortus Solutions, Corp
Much More
http://www.coldbox.org
https://github.com/coldbox/coldbox-platform
https://ortussolutions.atlassian.net/browse/LOGBOX
@coldbox
https://www.facebook.com/coldboxplatform
https://www.google.com/+ColdboxOrg
http://vimeo.com/channels/coldbox

Introduction

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

LogBox Manual - Version 5.x

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.

Versioning

<major>.<minor>.<patch>

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

  • 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

Reporting a Bug

Professional Open Source

  • Custom Development

  • Professional Support & Mentoring

  • Training

  • Server Tuning

  • Security Hardening

  • Code Reviews

Resources

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

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

Bug

Improvement

New Feature

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.

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

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

The LogBox help and discussion group can be found here:

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:

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

Official Site:

Source Code:

Bug Tracker:

Twitter:

Facebook:

Google+:

Vimeo Channel:

You can find the release version information here:

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

[] - deprecate logbox xml support

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

Log4j
Semantic Versioning
Apache 2
https://groups.google.com/forum/#!forum/coldbox
https://github.com/coldbox/coldbox-platform
bugs@coldbox.org
https://ortussolutions.atlassian.net/browse/LOGBOX
Ortus Solutions, Corp
Much More
http://www.coldbox.org
https://github.com/coldbox/coldbox-platform
https://ortussolutions.atlassian.net/browse/LOGBOX
@coldbox
https://www.facebook.com/coldboxplatform
https://www.google.com/+ColdboxOrg
http://vimeo.com/channels/coldbox
https://ortussolutions.atlassian.net/browse/LOGBOX/fixforversion/12302
LOGBOX-14
LOGBOX-13
LOGBOX-15

What's New With 2.1.0

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

Improvement

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

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.

What's New With 5.3.0

Improvements

About This Book

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

  • 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

Charitable Proceeds

Shalom Children's Home

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

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.

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

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

[] - 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

[] - Console Appender sent error messages to error out

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

The source code for this book is hosted in GitHub: . You can freely contribute to it and submit pull requests. The contents of this book is copyright by 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 generation and running of examples are done via CommandBox: The ColdFusion (CFML) CLI, Package Manager, REPL -

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.

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 () 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.

LOGBOX-19
LOGBOX-21
LOGBOX-16
LOGBOX-20
LOGBOX-26
LOGBOX-28
LOGBOX-32
https://github.com/ortus-docs/logbox-docs
Ortus Solutions, Corp
https://www.ortussolutions.com/products/commandbox
GitHub repository
http://www.harvesting.org/
http://www.harvesting.org/
// Simple Config
config = new logbox.system.logging.config.LogBoxConfig();
// Create logbox instance
logBox = new logbox.system.logging.LogBox( config );

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:

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.

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!

// Any appender can be asynchronous!
dbDebugger = {
    class="logbox.system.logging.appenders.DBAppender",
    properties={
        dsn="blog",
        table="logs",
        autocreate=true,
        textDBType="NCLOB",
        async=true
    }
}

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

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

Configuration CFC approach

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

}

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.

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:

// Appenders
config.appender(
  name="console",
  class="logbox.system.logging.appenders.ConsoleAppender"
);
config.appender(
  name="file",
  class="logbox.system.logging.appenders.FileAppender",
  properties={ filePath="/logs" }
)

And some categories like this:

// 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"
);

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

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

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.

Category

Configured Levels

Assigned Levels

Appenders

root

FATAL-DEBUG

FATAL-DEBUG

console,file

coldbox.system

INFO-DEBUG

INFO-DEBUG

console

coldbox.system.plugins

ERROR

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

*

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:

Category

Configured Levels

Assigned Levels

root

FATAL-DEBUG

FATAL-DEBUG

x

NONE

FATAL-DEBUG from root

x.y

INFO

INFO

x.y.z

NONE

INFO from x.y

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.

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).

//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"
);

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.

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.

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.

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:

logbox = new logbox.system.logging.LogBox();

CommandBox Installation

# Latest CommandBox
box install logbox

# Bleeding Edge
box install logbox@be

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

You can leverage to install the standalone version of LogBox

Download LogBox Standalone
API Docs
CommandBox
LogBox Docs Source
LogBox Planning Board
Log4J
LogStash

Author

Luis Fernando Majano Lainez

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

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 . You can read his blog at

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 , Mexico, he went back to his home country where he worked as the COO of. 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.

San Salvador, El Salvador
Florida International University
Ortus Solutions
Inland Empire
www.luismajano.com
ITESM
Industrias Bendek S.A.

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

  2. Creating the LogBoxConfig object and interacting with its methods

LogBox Refcard

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

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()

  • public void warn()

  • public void error()

  • public void fatal()

  • public void off()

https://github.com/ColdBox/cbox-refcards/raw/master/LogBox/LogBox-Refcard.pdf
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" );

Logger

Logbox

Logger Category Inheritance
Security Levels
Dynamic Appenders
LogBox
Ortus Solutions, Corp
LogBox
Ortus Solutions, Corp
Shalom Children's Home

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

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)

Example

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

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].)

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="*" );

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:

**
* A LogBox configuration data object
*/
component{

    function configure(){
        logBox={

        };
    }
}

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:

Key

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

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

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:

init([CFCConfig,CFCConfigPath])
  • CFCConfig : The object instance that has the logbox configuration data

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

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

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.

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:

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)

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.

//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" );

Adding Appenders

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

Parameters:

Examples

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

Name

Description

name

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].)

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

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!

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.

EmailAppender

Property

Type

Required

Default

Description

subject

string

true

---

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

from

string

true

---

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

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

this.logLevels

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

Utility Methods

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.

void setLevelMin(level)

Set the minimum severity level.

numeric getLevelMax()

Get the maximum severity level.

void setLevelMax(level)

Set the maximum severity level.

Logging Methods

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.

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

Log any kind of message.

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.

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

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

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

CFAppender

Property

Type

Required

Default

Description

logType

string(file or application)

false

file

The type of cflog to use: file or application.

fileName

string

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

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

$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.

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:

// User.cfc
component{

    function $toString(){
        // return my representation as a comma list of values of my properties
        return "#getName()#,#getAge()#,#getEmail()#";
    }
}

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 = userService.getUser( rc.id );

// need to log it.
if( log.canDebug() ){
    log.debug( "User just got logged in right now!", user );
}

SocketAppender

Property

Type

Requierd

Default

Description

host

string

true

The host to connect to

port

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

ScopeAppender

Property

Type

Required

Default

Description

scope

string

false

request

The scope to persist to, any valid CF scope.

key

string

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)

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.

Using LogBox

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

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

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.

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 () in order to get a listing of all available methods.

http://www.coldbox.org/api

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:

      • 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).

FileAppender

Property

Type

Required

Default

Description

filePath

string

true

---

The location of where to store the log file

filename

string

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

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

RollingFileAppender

Property

Type

Required

Default

Description

filePath

string

true

---

The location of where to store the log file

filename

string

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

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

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:

component{

    function configure(){
        logBox = {};

        // Define Appenders
        logBox.appenders = {
            console = { class="coldbox.system.logging.appenders.DummyAppender" }
        };

        // Root Logger
        logBox.root = {
            levelmax="OFF",
            levelMin="OFF",
            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.

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.

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

Helper Methods

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

Properties Methods

Utility Methods

Layout Methods

DBAppender

The columns needed or created in the table are

  • id : UUID

  • severity : string

  • category : string

  • logdate : timestamp

  • appendername : string

  • message : string

  • extrainfo : string

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

Method

Description

struct getProperties()

Get the entire properties struct.

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.

Property

Type

Required

Default

Description

dsn

string

true

---

The dsn to use for logging

table

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)

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}

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

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

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

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

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.

onUnRegistration()

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

init()

The signature of the init method is the following:

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

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:

<---  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()

The signature of the logMessage method is the following:

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

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

  • severity

  • extraInfo

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

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

onRegistration() & onUnregistration()

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

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

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

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

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.

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.

Instance Members

Every layout has access to the following public variables:

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).

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?

Instance Members

Every Appender has access to the following public variables:

Property

Description

this.logLevels

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