330 lines
12 KiB
C
330 lines
12 KiB
C
|
|
// -*- C++ -*-
|
||
|
|
// Module: Log4CPLUS
|
||
|
|
// File: ndc.h
|
||
|
|
// Created: 6/2001
|
||
|
|
// Author: Tad E. Smith
|
||
|
|
//
|
||
|
|
//
|
||
|
|
// Copyright 2001-2017 Tad E. Smith
|
||
|
|
//
|
||
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
|
// you may not use this file except in compliance with the License.
|
||
|
|
// You may obtain a copy of the License at
|
||
|
|
//
|
||
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||
|
|
//
|
||
|
|
// Unless required by applicable law or agreed to in writing, software
|
||
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
|
// See the License for the specific language governing permissions and
|
||
|
|
// limitations under the License.
|
||
|
|
|
||
|
|
/** @file
|
||
|
|
* This header defined the NDC class.
|
||
|
|
*/
|
||
|
|
|
||
|
|
#ifndef _LO4CPLUS_NDC_HEADER_
|
||
|
|
#define _LO4CPLUS_NDC_HEADER_
|
||
|
|
|
||
|
|
#include <log4cplus/config.hxx>
|
||
|
|
|
||
|
|
#if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE)
|
||
|
|
#pragma once
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#include <log4cplus/tstring.h>
|
||
|
|
|
||
|
|
#include <map>
|
||
|
|
#include <deque>
|
||
|
|
|
||
|
|
|
||
|
|
namespace log4cplus {
|
||
|
|
// Forward declarations
|
||
|
|
struct DiagnosticContext;
|
||
|
|
typedef std::deque<DiagnosticContext> DiagnosticContextStack;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* The NDC class implements <i>nested diagnostic contexts</i> as
|
||
|
|
* defined by Neil Harrison in the article "Patterns for Logging
|
||
|
|
* Diagnostic Messages" part of the book <i>"Pattern Languages of
|
||
|
|
* Program Design 3"</i> edited by Martin et al.
|
||
|
|
*
|
||
|
|
* A Nested Diagnostic Context, or NDC in short, is an instrument
|
||
|
|
* to distinguish interleaved log output from different sources. Log
|
||
|
|
* output is typically interleaved when a server handles multiple
|
||
|
|
* clients near-simultaneously.
|
||
|
|
*
|
||
|
|
* Interleaved log output can still be meaningful if each log entry
|
||
|
|
* from different contexts had a distinctive stamp. This is where NDCs
|
||
|
|
* come into play.
|
||
|
|
*
|
||
|
|
* <em><b>Note that NDCs are managed on a per thread
|
||
|
|
* basis</b></em>. NDC operations such as {@link #push}, {@link
|
||
|
|
* #pop}, {@link #clear}, {@link #getDepth} and {@link #setMaxDepth}
|
||
|
|
* affect the NDC of the <em>current</em> thread only. NDCs of other
|
||
|
|
* threads remain unaffected.
|
||
|
|
*
|
||
|
|
* For example, a server can build a per client request NDC
|
||
|
|
* consisting the clients host name and other information contained in
|
||
|
|
* the the request. <em>Cookies</em> are another source of distinctive
|
||
|
|
* information. To build an NDC one uses the {@link #push}
|
||
|
|
* operation. Simply put,
|
||
|
|
*
|
||
|
|
* - Contexts can be nested.
|
||
|
|
* - When entering a context, call `push()`. As a side effect, if
|
||
|
|
* there is no nested diagnostic context for the current thread,
|
||
|
|
* this method will create it.
|
||
|
|
* - When leaving a context, call `pop()`.
|
||
|
|
* - When exiting a thread make sure to call `remove()`.
|
||
|
|
*
|
||
|
|
* There is no penalty for forgetting to match each push()
|
||
|
|
* operation with a corresponding pop(), except the obvious
|
||
|
|
* mismatch between the real application context and the context
|
||
|
|
* set in the NDC. Use of the NDCContextCreator class can
|
||
|
|
* automate this process and make your code exception-safe.
|
||
|
|
*
|
||
|
|
* If configured to do so, {@link log4cplus::PatternLayout} and
|
||
|
|
* {@link log4cplus::TTCCLayout} instances automatically retrieve
|
||
|
|
* the nested diagnostic context for the current thread without
|
||
|
|
* any user intervention. Hence, even if a server is serving
|
||
|
|
* multiple clients simultaneously, the logs emanating from the
|
||
|
|
* same code (belonging to the same logger) can still be
|
||
|
|
* distinguished because each client request will have a different
|
||
|
|
* NDC tag.
|
||
|
|
*
|
||
|
|
* Heavy duty systems should call the {@link #remove} method when
|
||
|
|
* leaving the run method of a thread. This ensures that the memory
|
||
|
|
* used by the thread can be freed.
|
||
|
|
*
|
||
|
|
* A thread may inherit the nested diagnostic context of another
|
||
|
|
* (possibly parent) thread using the {@link #inherit inherit}
|
||
|
|
* method. A thread may obtain a copy of its NDC with the {@link
|
||
|
|
* #cloneStack cloneStack} method and pass the reference to any other
|
||
|
|
* thread, in particular to a child.
|
||
|
|
*/
|
||
|
|
class LOG4CPLUS_EXPORT NDC
|
||
|
|
{
|
||
|
|
public:
|
||
|
|
/**
|
||
|
|
* Clear any nested diagnostic information if any. This method is
|
||
|
|
* useful in cases where the same thread can be potentially used
|
||
|
|
* over and over in different unrelated contexts.
|
||
|
|
*
|
||
|
|
* This method is equivalent to calling the {@link #setMaxDepth}
|
||
|
|
* method with a zero <code>maxDepth</code> argument.
|
||
|
|
*/
|
||
|
|
void clear();
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Clone the diagnostic context for the current thread.
|
||
|
|
*
|
||
|
|
* Internally a diagnostic context is represented as a stack. A
|
||
|
|
* given thread can supply the stack (i.e. diagnostic context) to a
|
||
|
|
* child thread so that the child can inherit the parent thread's
|
||
|
|
* diagnostic context.
|
||
|
|
*
|
||
|
|
* The child thread uses the {@link #inherit inherit} method to
|
||
|
|
* inherit the parent's diagnostic context.
|
||
|
|
*
|
||
|
|
* @return Stack A clone of the current thread's diagnostic context.
|
||
|
|
*/
|
||
|
|
DiagnosticContextStack cloneStack() const;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Inherit the diagnostic context of another thread.
|
||
|
|
*
|
||
|
|
* The parent thread can obtain a reference to its diagnostic
|
||
|
|
* context using the {@link #cloneStack} method. It should
|
||
|
|
* communicate this information to its child so that it may inherit
|
||
|
|
* the parent's diagnostic context.
|
||
|
|
*
|
||
|
|
* The parent's diagnostic context is cloned before being
|
||
|
|
* inherited. In other words, once inherited, the two diagnostic
|
||
|
|
* contexts can be managed independently.
|
||
|
|
*
|
||
|
|
* @param stack The diagnostic context of the parent thread.
|
||
|
|
*/
|
||
|
|
void inherit(const DiagnosticContextStack& stack);
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Used when printing the diagnostic context.
|
||
|
|
*/
|
||
|
|
log4cplus::tstring const & get() const;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get the current nesting depth of this diagnostic context.
|
||
|
|
*
|
||
|
|
* @see #setMaxDepth
|
||
|
|
*/
|
||
|
|
std::size_t getDepth() const;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Clients should call this method before leaving a diagnostic
|
||
|
|
* context.
|
||
|
|
*
|
||
|
|
* The returned value is the value that was pushed last. If no
|
||
|
|
* context is available, then the empty string is returned. If
|
||
|
|
* each call to `push()` is paired with a call to `pop()`
|
||
|
|
* (even in presence of thrown exceptions), the last `pop()`
|
||
|
|
* call frees the memory used by NDC for this
|
||
|
|
* thread. Otherwise, `remove()` must be called at the end of
|
||
|
|
* the thread to free the memory used by NDC for the thread.
|
||
|
|
*
|
||
|
|
* @return String The innermost diagnostic context.
|
||
|
|
*
|
||
|
|
* @see NDCContextCreator, remove(), push()
|
||
|
|
*/
|
||
|
|
log4cplus::tstring pop();
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Same as pop() but without the return value.
|
||
|
|
*/
|
||
|
|
void pop_void ();
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Looks at the last diagnostic context at the top of this NDC
|
||
|
|
* without removing it.
|
||
|
|
*
|
||
|
|
* The returned value is the value that was pushed last. If no
|
||
|
|
* context is available, then the empty string is returned.
|
||
|
|
*
|
||
|
|
* @return String The innermost diagnostic context.
|
||
|
|
*/
|
||
|
|
log4cplus::tstring const & peek() const;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Push new diagnostic context information for the current thread.
|
||
|
|
*
|
||
|
|
* The contents of the <code>message</code> parameter is
|
||
|
|
* determined solely by the client. Each call to push() should
|
||
|
|
* be paired with a call to pop().
|
||
|
|
*
|
||
|
|
* @param message The new diagnostic context information.
|
||
|
|
*
|
||
|
|
* @see NDCContextCreator, pop(), remove()
|
||
|
|
*/
|
||
|
|
void push(const log4cplus::tstring& message);
|
||
|
|
void push(tchar const * message);
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Remove the diagnostic context for this thread.
|
||
|
|
*
|
||
|
|
* Each thread that created a diagnostic context by calling
|
||
|
|
* push() should call this method before exiting. Otherwise,
|
||
|
|
* the memory used by the thread cannot be reclaimed. It is
|
||
|
|
* possible to omit this call if and only if each push() call
|
||
|
|
* is always paired with a pop() call (even in presence of
|
||
|
|
* thrown exceptions). Then the memory used by NDC will be
|
||
|
|
* returned by the last pop() call and a call to remove() will
|
||
|
|
* be no-op.
|
||
|
|
*/
|
||
|
|
void remove();
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Set maximum depth of this diagnostic context. If the
|
||
|
|
* current depth is smaller or equal to `maxDepth`, then no
|
||
|
|
* action is taken.
|
||
|
|
*
|
||
|
|
* This method is a convenient alternative to multiple `pop()`
|
||
|
|
* calls. Moreover, it is often the case that at the end of
|
||
|
|
* complex call sequences, the depth of the NDC is
|
||
|
|
* unpredictable. The `setMaxDepth()` method circumvents this
|
||
|
|
* problem.
|
||
|
|
*
|
||
|
|
* For example, the combination
|
||
|
|
*
|
||
|
|
* ~~~~{.c}
|
||
|
|
* void foo() {
|
||
|
|
* NDC & ndc = getNDC();
|
||
|
|
* std::size_t depth = ndc.getDepth();
|
||
|
|
* //... complex sequence of calls
|
||
|
|
* ndc.setMaxDepth(depth);
|
||
|
|
* }
|
||
|
|
* ~~~~
|
||
|
|
*
|
||
|
|
* ensures that between the entry and exit of foo the depth of the
|
||
|
|
* diagnostic stack is conserved.
|
||
|
|
*
|
||
|
|
* \note Use of the NDCContextCreator class will solve this
|
||
|
|
* particular problem.
|
||
|
|
*
|
||
|
|
* \see NDC::getDepth()
|
||
|
|
*/
|
||
|
|
void setMaxDepth(std::size_t maxDepth);
|
||
|
|
|
||
|
|
// Public ctor but only to be used by internal::DefaultContext.
|
||
|
|
NDC();
|
||
|
|
|
||
|
|
// Dtor
|
||
|
|
virtual ~NDC();
|
||
|
|
|
||
|
|
private:
|
||
|
|
// Methods
|
||
|
|
LOG4CPLUS_PRIVATE static DiagnosticContextStack* getPtr();
|
||
|
|
|
||
|
|
template <typename StringType>
|
||
|
|
LOG4CPLUS_PRIVATE
|
||
|
|
void push_worker (StringType const &);
|
||
|
|
|
||
|
|
// Disallow construction (and copying) except by getNDC()
|
||
|
|
NDC(const NDC&);
|
||
|
|
NDC& operator=(const NDC&);
|
||
|
|
};
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Return a reference to the singleton object.
|
||
|
|
*/
|
||
|
|
LOG4CPLUS_EXPORT NDC& getNDC();
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
* This is the internal object that is stored on the NDC stack.
|
||
|
|
*/
|
||
|
|
struct LOG4CPLUS_EXPORT DiagnosticContext
|
||
|
|
{
|
||
|
|
// Ctors
|
||
|
|
DiagnosticContext(const log4cplus::tstring& message,
|
||
|
|
DiagnosticContext const * parent);
|
||
|
|
DiagnosticContext(tchar const * message,
|
||
|
|
DiagnosticContext const * parent);
|
||
|
|
DiagnosticContext(const log4cplus::tstring& message);
|
||
|
|
DiagnosticContext(tchar const * message);
|
||
|
|
DiagnosticContext(DiagnosticContext const &);
|
||
|
|
DiagnosticContext & operator = (DiagnosticContext const &);
|
||
|
|
DiagnosticContext(DiagnosticContext &&);
|
||
|
|
DiagnosticContext & operator = (DiagnosticContext &&);
|
||
|
|
|
||
|
|
void swap (DiagnosticContext &);
|
||
|
|
|
||
|
|
// Data
|
||
|
|
log4cplus::tstring message; /*!< The message at this context level. */
|
||
|
|
log4cplus::tstring fullMessage; /*!< The entire message stack. */
|
||
|
|
};
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
* This class ensures that a `NDC::push()` call is always matched
|
||
|
|
* with a `NDC::pop()` call even in the face of exceptions.
|
||
|
|
*/
|
||
|
|
class LOG4CPLUS_EXPORT NDCContextCreator {
|
||
|
|
public:
|
||
|
|
/** Pushes <code>msg</code> onto the NDC stack. */
|
||
|
|
explicit NDCContextCreator(const log4cplus::tstring& msg);
|
||
|
|
explicit NDCContextCreator(tchar const * msg);
|
||
|
|
|
||
|
|
NDCContextCreator() = delete;
|
||
|
|
NDCContextCreator(NDCContextCreator const &) = delete;
|
||
|
|
NDCContextCreator(NDCContextCreator &&) = delete;
|
||
|
|
NDCContextCreator & operator= (NDCContextCreator const &) = delete;
|
||
|
|
NDCContextCreator & operator= (NDCContextCreator &&) = delete;
|
||
|
|
|
||
|
|
/** Pops the NDC stack. */
|
||
|
|
~NDCContextCreator();
|
||
|
|
};
|
||
|
|
|
||
|
|
} // end namespace log4cplus
|
||
|
|
|
||
|
|
|
||
|
|
#endif // _LO4CPLUS_NDC_HEADER_
|