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.core.config.plugins.PluginBuilderAttribute;
029import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
030import org.apache.logging.log4j.status.StatusLogger;
031
032/**
033 * Abstract base class for Layouts.
034 *
035 * @param <T>
036 *            The Class that the Layout will format the LogEvent into.
037 */
038public abstract class AbstractLayout<T extends Serializable> implements Layout<T> {
039
040    /**
041     * Subclasses can extend this abstract Builder.
042     *
043     * @param <B> The type to build.
044     */
045    public abstract static class Builder<B extends Builder<B>> {
046
047        @PluginConfiguration
048        private Configuration configuration;
049
050        @PluginBuilderAttribute
051        private byte[] footer;
052
053        @PluginBuilderAttribute
054        private byte[] header;
055
056        @SuppressWarnings("unchecked")
057        public B asBuilder() {
058            return (B) this;
059        }
060
061        public Configuration getConfiguration() {
062            return configuration;
063        }
064
065        public byte[] getFooter() {
066            return footer;
067        }
068
069        public byte[] getHeader() {
070            return header;
071        }
072
073        public B setConfiguration(final Configuration configuration) {
074            this.configuration = configuration;
075            return asBuilder();
076        }
077
078        public B setFooter(final byte[] footer) {
079            this.footer = footer;
080            return asBuilder();
081        }
082
083        public B setHeader(final byte[] header) {
084            this.header = header;
085            return asBuilder();
086        }
087
088    }
089
090    /**
091     * Allow subclasses access to the status logger without creating another instance.
092     */
093    protected static final Logger LOGGER = StatusLogger.getLogger();
094
095    /**
096     * The current Configuration.
097     */
098    protected final Configuration configuration;
099
100    /**
101     * The number of events successfully processed by this layout.
102     */
103    protected long eventCount;
104
105    /**
106     * The footer to add when the stream is closed. May be null.
107     */
108    protected final byte[] footer;
109
110    /**
111     * The header to include when the stream is opened. May be null.
112     */
113    protected final byte[] header;
114
115    /**
116     * Constructs a layout with an optional header and footer.
117     *
118     * @param header
119     *            The header to include when the stream is opened. May be null.
120     * @param footer
121     *            The footer to add when the stream is closed. May be null.
122     * @deprecated Use {@link #AbstractLayout(Configuration, byte[], byte[])}
123     */
124    @Deprecated
125    public AbstractLayout(final byte[] header, final byte[] footer) {
126        this(null, header, footer);
127    }
128
129    /**
130     * Constructs a layout with an optional header and footer.
131     *
132     * @param configuration
133     *            The configuration
134     * @param header
135     *            The header to include when the stream is opened. May be null.
136     * @param footer
137     *            The footer to add when the stream is closed. May be null.
138     */
139    public AbstractLayout(final Configuration configuration, final byte[] header, final byte[] footer) {
140        super();
141        this.configuration = configuration;
142        this.header = header;
143        this.footer = footer;
144    }
145
146    public Configuration getConfiguration() {
147        return configuration;
148    }
149
150    @Override
151    public Map<String, String> getContentFormat() {
152        return new HashMap<>();
153    }
154
155    /**
156     * Returns the footer, if one is available.
157     *
158     * @return A byte array containing the footer.
159     */
160    @Override
161    public byte[] getFooter() {
162        return footer;
163    }
164
165    /**
166     * Returns the header, if one is available.
167     *
168     * @return A byte array containing the header.
169     */
170    @Override
171    public byte[] getHeader() {
172        return header;
173    }
174
175    protected void markEvent() {
176        eventCount++;
177    }
178
179    /**
180     * Encodes the specified source LogEvent to some binary representation and writes the result to the specified
181     * destination.
182     * <p>
183     * The default implementation of this method delegates to the {@link #toByteArray(LogEvent)} method which allocates
184     * temporary objects.
185     * </p><p>
186     * Subclasses can override this method to provide a garbage-free implementation. For text-based layouts,
187     * {@code AbstractStringLayout} provides various convenience methods to help with this:
188     * </p>
189     * <pre>@Plugin(name = "MyLayout", category = Node.CATEGORY, elementType = Layout.ELEMENT_TYPE, printObject = true)
190     * public final class MyLayout extends AbstractStringLayout {
191     *     @Override
192     *     public void encode(LogEvent event, ByteBufferDestination destination) {
193     *         StringBuilder text = getStringBuilder();
194     *         convertLogEventToText(event, text);
195     *         getStringBuilderEncoder().encode(text, destination);
196     *     }
197     *
198     *     private void convertLogEventToText(LogEvent event, StringBuilder destination) {
199     *         ... // append a text representation of the log event to the StringBuilder
200     *     }
201     * }
202     * </pre>
203     *
204     * @param event the LogEvent to encode.
205     * @param destination holds the ByteBuffer to write into.
206     * @see AbstractStringLayout#getStringBuilder()
207     * @see AbstractStringLayout#getStringBuilderEncoder()
208     */
209    @Override
210    public void encode(final LogEvent event, final ByteBufferDestination destination) {
211        final byte[] data = toByteArray(event);
212        writeTo(data, 0, data.length, destination);
213    }
214
215    /**
216     * Writes the specified data to the specified destination.
217     *
218     * @param data the data to write
219     * @param offset where to start in the specified data array
220     * @param length the number of bytes to write
221     * @param destination the {@code ByteBufferDestination} to write to
222     */
223    public static void writeTo(final byte[] data, int offset, int length, final ByteBufferDestination destination) {
224        int chunk = 0;
225        synchronized (destination) {
226            ByteBuffer buffer = destination.getByteBuffer();
227            do {
228                if (length > buffer.remaining()) {
229                    buffer = destination.drain(buffer);
230                }
231                chunk = Math.min(length, buffer.remaining());
232                buffer.put(data, offset, chunk);
233                offset += chunk;
234                length -= chunk;
235            } while (length > 0);
236        }
237    }
238}