001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache license, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the license for the specific language governing permissions and
015 * limitations under the license.
016 */
017package org.apache.logging.log4j.core.layout;
018
019import java.io.Serializable;
020import java.nio.ByteBuffer;
021import java.util.HashMap;
022import java.util.Map;
023
024import org.apache.logging.log4j.Logger;
025import org.apache.logging.log4j.core.Layout;
026import org.apache.logging.log4j.core.LogEvent;
027import org.apache.logging.log4j.core.config.Configuration;
028import org.apache.logging.log4j.status.StatusLogger;
029
030/**
031 * Abstract base class for Layouts.
032 *
033 * @param <T>
034 *            The Class that the Layout will format the LogEvent into.
035 */
036public abstract class AbstractLayout<T extends Serializable> implements Layout<T> {
037
038    /**
039     * Allow subclasses access to the status logger without creating another instance.
040     */
041    protected static final Logger LOGGER = StatusLogger.getLogger();
042
043    /**
044     * The current Configuration.
045     */
046    protected final Configuration configuration;
047
048    /**
049     * The number of events successfully processed by this layout.
050     */
051    protected long eventCount;
052
053    /**
054     * The footer to add when the stream is closed. May be null.
055     */
056    protected final byte[] footer;
057
058    /**
059     * The header to include when the stream is opened. May be null.
060     */
061    protected final byte[] header;
062
063    /**
064     * Constructs a layout with an optional header and footer.
065     *
066     * @param header
067     *            The header to include when the stream is opened. May be null.
068     * @param footer
069     *            The footer to add when the stream is closed. May be null.
070     * @deprecated Use {@link #AbstractLayout(Configuration, byte[], byte[])}
071     */
072    @Deprecated
073    public AbstractLayout(final byte[] header, final byte[] footer) {
074        this(null, header, footer);
075    }
076
077    /**
078     * Constructs a layout with an optional header and footer.
079     *
080     * @param configuration
081     *            The configuration
082     * @param header
083     *            The header to include when the stream is opened. May be null.
084     * @param footer
085     *            The footer to add when the stream is closed. May be null.
086     */
087    public AbstractLayout(final Configuration configuration, final byte[] header, final byte[] footer) {
088        super();
089        this.configuration = configuration;
090        this.header = header;
091        this.footer = footer;
092    }
093
094    public Configuration getConfiguration() {
095        return configuration;
096    }
097
098    @Override
099    public Map<String, String> getContentFormat() {
100        return new HashMap<>();
101    }
102
103    /**
104     * Returns the footer, if one is available.
105     *
106     * @return A byte array containing the footer.
107     */
108    @Override
109    public byte[] getFooter() {
110        return footer;
111    }
112
113    /**
114     * Returns the header, if one is available.
115     *
116     * @return A byte array containing the header.
117     */
118    @Override
119    public byte[] getHeader() {
120        return header;
121    }
122
123    protected void markEvent() {
124        eventCount++;
125    }
126
127    /**
128     * Encodes the specified source LogEvent to some binary representation and writes the result to the specified
129     * destination.
130     * <p>
131     * The default implementation of this method delegates to the {@link #toByteArray(LogEvent)} method which allocates
132     * temporary objects.
133     * </p><p>
134     * Subclasses can override this method to provide a garbage-free implementation. For text-based layouts,
135     * {@code AbstractStringLayout} provides various convenience methods to help with this:
136     * </p>
137     * <pre>@Plugin(name = "MyLayout", category = Node.CATEGORY, elementType = Layout.ELEMENT_TYPE, printObject = true)
138     * public final class MyLayout extends AbstractStringLayout {
139     *     @Override
140     *     public void encode(LogEvent event, ByteBufferDestination destination) {
141     *         StringBuilder text = getStringBuilder();
142     *         convertLogEventToText(event, text);
143     *         getStringBuilderEncoder().encode(text, destination);
144     *     }
145     *
146     *     private void convertLogEventToText(LogEvent event, StringBuilder destination) {
147     *         ... // append a text representation of the log event to the StringBuilder
148     *     }
149     * }
150     * </pre>
151     *
152     * @param event the LogEvent to encode.
153     * @param destination holds the ByteBuffer to write into.
154     * @see AbstractStringLayout#getStringBuilder()
155     * @see AbstractStringLayout#getStringBuilderEncoder()
156     */
157    @Override
158    public void encode(final LogEvent event, final ByteBufferDestination destination) {
159        final byte[] data = toByteArray(event);
160        writeTo(data, 0, data.length, destination);
161    }
162
163    /**
164     * Writes the specified data to the specified destination.
165     *
166     * @param data the data to write
167     * @param offset where to start in the specified data array
168     * @param length the number of bytes to write
169     * @param destination the {@code ByteBufferDestination} to write to
170     */
171    public static void writeTo(final byte[] data, int offset, int length, final ByteBufferDestination destination) {
172        int chunk = 0;
173        ByteBuffer buffer = destination.getByteBuffer();
174        do {
175            if (length > buffer.remaining()) {
176                buffer = destination.drain(buffer);
177            }
178            chunk = Math.min(length, buffer.remaining());
179            buffer.put(data, offset, chunk);
180            offset += chunk;
181            length -= chunk;
182        } while (length > 0);
183    }
184}