#region Apache License
|
//
|
// Licensed to the Apache Software Foundation (ASF) under one or more
|
// contributor license agreements. See the NOTICE file distributed with
|
// this work for additional information regarding copyright ownership.
|
// The ASF licenses this file to you 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.
|
//
|
#endregion
|
|
using System;
|
|
using log4net.Core;
|
|
namespace log4net.Util
|
{
|
/// <summary>
|
/// A fixed size rolling buffer of logging events.
|
/// </summary>
|
/// <remarks>
|
/// <para>
|
/// An array backed fixed size leaky bucket.
|
/// </para>
|
/// </remarks>
|
/// <author>Nicko Cadell</author>
|
/// <author>Gert Driesen</author>
|
public class CyclicBuffer
|
{
|
#region Public Instance Constructors
|
|
/// <summary>
|
/// Constructor
|
/// </summary>
|
/// <param name="maxSize">The maximum number of logging events in the buffer.</param>
|
/// <remarks>
|
/// <para>
|
/// Initializes a new instance of the <see cref="CyclicBuffer" /> class with
|
/// the specified maximum number of buffered logging events.
|
/// </para>
|
/// </remarks>
|
/// <exception cref="ArgumentOutOfRangeException">The <paramref name="maxSize"/> argument is not a positive integer.</exception>
|
public CyclicBuffer(int maxSize)
|
{
|
if (maxSize < 1)
|
{
|
throw SystemInfo.CreateArgumentOutOfRangeException("maxSize", (object)maxSize, "Parameter: maxSize, Value: [" + maxSize + "] out of range. Non zero positive integer required");
|
}
|
|
m_maxSize = maxSize;
|
m_events = new LoggingEvent[maxSize];
|
m_first = 0;
|
m_last = 0;
|
m_numElems = 0;
|
}
|
|
#endregion Public Instance Constructors
|
|
#region Public Instance Methods
|
|
/// <summary>
|
/// Appends a <paramref name="loggingEvent"/> to the buffer.
|
/// </summary>
|
/// <param name="loggingEvent">The event to append to the buffer.</param>
|
/// <returns>The event discarded from the buffer, if the buffer is full, otherwise <c>null</c>.</returns>
|
/// <remarks>
|
/// <para>
|
/// Append an event to the buffer. If the buffer still contains free space then
|
/// <c>null</c> is returned. If the buffer is full then an event will be dropped
|
/// to make space for the new event, the event dropped is returned.
|
/// </para>
|
/// </remarks>
|
public LoggingEvent Append(LoggingEvent loggingEvent)
|
{
|
if (loggingEvent == null)
|
{
|
throw new ArgumentNullException("loggingEvent");
|
}
|
|
lock(this)
|
{
|
// save the discarded event
|
LoggingEvent discardedLoggingEvent = m_events[m_last];
|
|
// overwrite the last event position
|
m_events[m_last] = loggingEvent;
|
if (++m_last == m_maxSize)
|
{
|
m_last = 0;
|
}
|
|
if (m_numElems < m_maxSize)
|
{
|
m_numElems++;
|
}
|
else if (++m_first == m_maxSize)
|
{
|
m_first = 0;
|
}
|
|
if (m_numElems < m_maxSize)
|
{
|
// Space remaining
|
return null;
|
}
|
else
|
{
|
// Buffer is full and discarding an event
|
return discardedLoggingEvent;
|
}
|
}
|
}
|
|
/// <summary>
|
/// Get and remove the oldest event in the buffer.
|
/// </summary>
|
/// <returns>The oldest logging event in the buffer</returns>
|
/// <remarks>
|
/// <para>
|
/// Gets the oldest (first) logging event in the buffer and removes it
|
/// from the buffer.
|
/// </para>
|
/// </remarks>
|
public LoggingEvent PopOldest()
|
{
|
lock(this)
|
{
|
LoggingEvent ret = null;
|
if (m_numElems > 0)
|
{
|
m_numElems--;
|
ret = m_events[m_first];
|
m_events[m_first] = null;
|
if (++m_first == m_maxSize)
|
{
|
m_first = 0;
|
}
|
}
|
return ret;
|
}
|
}
|
|
/// <summary>
|
/// Pops all the logging events from the buffer into an array.
|
/// </summary>
|
/// <returns>An array of all the logging events in the buffer.</returns>
|
/// <remarks>
|
/// <para>
|
/// Get all the events in the buffer and clear the buffer.
|
/// </para>
|
/// </remarks>
|
public LoggingEvent[] PopAll()
|
{
|
lock(this)
|
{
|
LoggingEvent[] ret = new LoggingEvent[m_numElems];
|
|
if (m_numElems > 0)
|
{
|
if (m_first < m_last)
|
{
|
Array.Copy(m_events, m_first, ret, 0, m_numElems);
|
}
|
else
|
{
|
Array.Copy(m_events, m_first, ret, 0, m_maxSize - m_first);
|
Array.Copy(m_events, 0, ret, m_maxSize - m_first, m_last);
|
}
|
}
|
|
Clear();
|
|
return ret;
|
}
|
}
|
|
/// <summary>
|
/// Clear the buffer
|
/// </summary>
|
/// <remarks>
|
/// <para>
|
/// Clear the buffer of all events. The events in the buffer are lost.
|
/// </para>
|
/// </remarks>
|
public void Clear()
|
{
|
lock(this)
|
{
|
// Set all the elements to null
|
Array.Clear(m_events, 0, m_events.Length);
|
|
m_first = 0;
|
m_last = 0;
|
m_numElems = 0;
|
}
|
}
|
|
#if RESIZABLE_CYCLIC_BUFFER
|
/// <summary>
|
/// Resizes the cyclic buffer to <paramref name="newSize"/>.
|
/// </summary>
|
/// <param name="newSize">The new size of the buffer.</param>
|
/// <remarks>
|
/// <para>
|
/// Resize the cyclic buffer. Events in the buffer are copied into
|
/// the newly sized buffer. If the buffer is shrunk and there are
|
/// more events currently in the buffer than the new size of the
|
/// buffer then the newest events will be dropped from the buffer.
|
/// </para>
|
/// </remarks>
|
/// <exception cref="ArgumentOutOfRangeException">The <paramref name="newSize"/> argument is not a positive integer.</exception>
|
public void Resize(int newSize)
|
{
|
lock(this)
|
{
|
if (newSize < 0)
|
{
|
throw log4net.Util.SystemInfo.CreateArgumentOutOfRangeException("newSize", (object)newSize, "Parameter: newSize, Value: [" + newSize + "] out of range. Non zero positive integer required");
|
}
|
if (newSize == m_numElems)
|
{
|
return; // nothing to do
|
}
|
|
LoggingEvent[] temp = new LoggingEvent[newSize];
|
|
int loopLen = (newSize < m_numElems) ? newSize : m_numElems;
|
|
for(int i = 0; i < loopLen; i++)
|
{
|
temp[i] = m_events[m_first];
|
m_events[m_first] = null;
|
|
if (++m_first == m_numElems)
|
{
|
m_first = 0;
|
}
|
}
|
|
m_events = temp;
|
m_first = 0;
|
m_numElems = loopLen;
|
m_maxSize = newSize;
|
|
if (loopLen == newSize)
|
{
|
m_last = 0;
|
}
|
else
|
{
|
m_last = loopLen;
|
}
|
}
|
}
|
#endif
|
|
#endregion Public Instance Methods
|
|
#region Public Instance Properties
|
|
/// <summary>
|
/// Gets the <paramref name="i"/>th oldest event currently in the buffer.
|
/// </summary>
|
/// <value>The <paramref name="i"/>th oldest event currently in the buffer.</value>
|
/// <remarks>
|
/// <para>
|
/// If <paramref name="i"/> is outside the range 0 to the number of events
|
/// currently in the buffer, then <c>null</c> is returned.
|
/// </para>
|
/// </remarks>
|
public LoggingEvent this[int i]
|
{
|
get
|
{
|
lock(this)
|
{
|
if (i < 0 || i >= m_numElems)
|
{
|
return null;
|
}
|
|
return m_events[(m_first + i) % m_maxSize];
|
}
|
}
|
}
|
|
/// <summary>
|
/// Gets the maximum size of the buffer.
|
/// </summary>
|
/// <value>The maximum size of the buffer.</value>
|
/// <remarks>
|
/// <para>
|
/// Gets the maximum size of the buffer
|
/// </para>
|
/// </remarks>
|
public int MaxSize
|
{
|
get
|
{
|
lock(this)
|
{
|
return m_maxSize;
|
}
|
}
|
#if RESIZABLE_CYCLIC_BUFFER
|
set
|
{
|
/// Setting the MaxSize will cause the buffer to resize.
|
Resize(value);
|
}
|
#endif
|
}
|
|
/// <summary>
|
/// Gets the number of logging events in the buffer.
|
/// </summary>
|
/// <value>The number of logging events in the buffer.</value>
|
/// <remarks>
|
/// <para>
|
/// This number is guaranteed to be in the range 0 to <see cref="MaxSize"/>
|
/// (inclusive).
|
/// </para>
|
/// </remarks>
|
public int Length
|
{
|
get
|
{
|
lock(this)
|
{
|
return m_numElems;
|
}
|
}
|
}
|
|
#endregion Public Instance Properties
|
|
#region Private Instance Fields
|
|
private LoggingEvent[] m_events;
|
private int m_first;
|
private int m_last;
|
private int m_numElems;
|
private int m_maxSize;
|
|
#endregion Private Instance Fields
|
}
|
}
|