View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache license, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License. You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the license for the specific language governing permissions and
15   * limitations under the license.
16   */
17  package org.apache.logging.log4j.core.layout;
18  
19  import java.io.Serializable;
20  import java.nio.ByteBuffer;
21  import java.util.HashMap;
22  import java.util.Map;
23  
24  import org.apache.logging.log4j.Logger;
25  import org.apache.logging.log4j.core.Layout;
26  import org.apache.logging.log4j.core.LogEvent;
27  import org.apache.logging.log4j.core.config.Configuration;
28  import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
29  import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
30  import org.apache.logging.log4j.status.StatusLogger;
31  
32  /**
33   * Abstract base class for Layouts.
34   *
35   * @param <T>
36   *            The Class that the Layout will format the LogEvent into.
37   */
38  public abstract class AbstractLayout<T extends Serializable> implements Layout<T> {
39  
40      /**
41       * Subclasses can extend this abstract Builder.
42       *
43       * @param <B> The type to build.
44       */
45      public abstract static class Builder<B extends Builder<B>> {
46  
47          @PluginConfiguration
48          private Configuration configuration;
49  
50          @PluginBuilderAttribute
51          private byte[] footer;
52  
53          @PluginBuilderAttribute
54          private byte[] header;
55  
56          @SuppressWarnings("unchecked")
57          public B asBuilder() {
58              return (B) this;
59          }
60  
61          public Configuration getConfiguration() {
62              return configuration;
63          }
64  
65          public byte[] getFooter() {
66              return footer;
67          }
68  
69          public byte[] getHeader() {
70              return header;
71          }
72  
73          public B setConfiguration(final Configuration configuration) {
74              this.configuration = configuration;
75              return asBuilder();
76          }
77  
78          public B setFooter(final byte[] footer) {
79              this.footer = footer;
80              return asBuilder();
81          }
82  
83          public B setHeader(final byte[] header) {
84              this.header = header;
85              return asBuilder();
86          }
87  
88      }
89  
90      /**
91       * Allow subclasses access to the status logger without creating another instance.
92       */
93      protected static final Logger LOGGER = StatusLogger.getLogger();
94  
95      /**
96       * The current Configuration.
97       */
98      protected final Configuration configuration;
99  
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 }