() {
195 | @Override
196 | public int compare(InputLogEvent left, InputLogEvent right) {
197 | if (left.getTimestamp() < right.getTimestamp()) {
198 | return -1;
199 | } else if (left.getTimestamp() > right.getTimestamp()) {
200 | return 1;
201 | } else {
202 | return 0;
203 | }
204 | }
205 | };
206 |
207 | private final AbstractCloudWatchAppender parent;
208 |
209 | private String token = null;
210 | private volatile boolean started = false;
211 |
212 | Worker(AbstractCloudWatchAppender
parent) {
213 | this.parent = parent;
214 | }
215 |
216 | @Override
217 | public void run() {
218 | started = true;
219 | while (started) {
220 | List events = new LinkedList<>();
221 | try {
222 | Queues.drain(parent.queue, events, parent.maxBatchSize, parent.maxBatchTime, TimeUnit.MILLISECONDS);
223 | handle(events);
224 | } catch (InterruptedException ex) {
225 | handle(events);
226 | }
227 | }
228 |
229 | List remaining = new LinkedList<>();
230 | parent.queue.drainTo(remaining);
231 | for (List batch : Lists.partition(remaining, parent.maxBatchSize)) {
232 | handle(batch);
233 | }
234 |
235 | }
236 |
237 | public void stopGracefully() {
238 | started = false;
239 | }
240 |
241 | private void handle(List events) {
242 | if (!events.isEmpty()) {
243 | List sorted = ORDERING.immutableSortedCopy(events);
244 | PutLogEventsRequest request = new PutLogEventsRequest(parent.logGroup, parent.logStream, sorted);
245 | try {
246 | try {
247 | putEvents(request);
248 | } catch (DataAlreadyAcceptedException | InvalidSequenceTokenException ex) {
249 | putEvents(request);
250 | }
251 | } catch (Exception ex) {
252 | parent.addError(format("Failed to handle %d events", events.size()), ex);
253 | }
254 | }
255 | }
256 |
257 | private void putEvents(PutLogEventsRequest request) {
258 | try {
259 | PutLogEventsResult result = parent.logs.putLogEvents(request.withSequenceToken(token));
260 | token = result.getNextSequenceToken();
261 | } catch (DataAlreadyAcceptedException ex) {
262 | token = ex.getExpectedSequenceToken();
263 | throw ex;
264 | } catch (InvalidSequenceTokenException ex) {
265 | token = ex.getExpectedSequenceToken();
266 | throw ex;
267 | }
268 | }
269 | }
270 | }
271 |
--------------------------------------------------------------------------------
/logback-ext-cloudwatch-appender/src/main/java/org/eluder/logback/ext/cloudwatch/appender/CloudWatchAccessAppender.java:
--------------------------------------------------------------------------------
1 | package org.eluder.logback.ext.cloudwatch.appender;
2 |
3 | import ch.qos.logback.access.spi.IAccessEvent;
4 | import ch.qos.logback.core.filter.Filter;
5 | import org.eluder.logback.ext.aws.core.AwsSupport;
6 | import org.eluder.logback.ext.core.CommonEventAttributes;
7 |
8 | public class CloudWatchAccessAppender extends AbstractCloudWatchAppender {
9 |
10 | public CloudWatchAccessAppender() {
11 | super();
12 | }
13 |
14 | protected CloudWatchAccessAppender(AwsSupport awsSupport, Filter sdkLoggingFilter) {
15 | super(awsSupport, sdkLoggingFilter);
16 | }
17 |
18 | @Override
19 | protected CommonEventAttributes applyCommonEventAttributes(final IAccessEvent event) {
20 | return new CommonEventAttributes() {
21 | @Override
22 | public String getThreadName() {
23 | return event.getThreadName();
24 | }
25 |
26 | @Override
27 | public long getTimeStamp() {
28 | return event.getTimeStamp();
29 | }
30 | };
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/logback-ext-cloudwatch-appender/src/main/java/org/eluder/logback/ext/cloudwatch/appender/CloudWatchAppender.java:
--------------------------------------------------------------------------------
1 | package org.eluder.logback.ext.cloudwatch.appender;
2 |
3 | import ch.qos.logback.classic.spi.ILoggingEvent;
4 | import ch.qos.logback.core.filter.Filter;
5 | import org.eluder.logback.ext.aws.core.AwsSupport;
6 | import org.eluder.logback.ext.core.CommonEventAttributes;
7 |
8 | public class CloudWatchAppender extends AbstractCloudWatchAppender {
9 |
10 | public CloudWatchAppender() {
11 | super();
12 | }
13 |
14 | protected CloudWatchAppender(AwsSupport awsSupport, Filter sdkLoggingFilter) {
15 | super(awsSupport, sdkLoggingFilter);
16 | }
17 |
18 | @Override
19 | protected CommonEventAttributes applyCommonEventAttributes(final ILoggingEvent event) {
20 | return new CommonEventAttributes() {
21 | @Override
22 | public String getThreadName() {
23 | return event.getThreadName();
24 | }
25 |
26 | @Override
27 | public long getTimeStamp() {
28 | return event.getTimeStamp();
29 | }
30 | };
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/logback-ext-core/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 |
8 | logback-ext
9 | org.eluder.logback
10 | 1.0-SNAPSHOT
11 |
12 |
13 | logback-ext-core
14 |
15 |
16 |
17 | ch.qos.logback
18 | logback-core
19 |
20 |
21 | com.google.guava
22 | guava
23 | ${guava.version}
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/logback-ext-core/src/main/java/org/eluder/logback/ext/core/AppenderExecutors.java:
--------------------------------------------------------------------------------
1 | package org.eluder.logback.ext.core;
2 |
3 | /*
4 | * #[license]
5 | * logback-ext-core
6 | * %%
7 | * Copyright (C) 2014 - 2015 Tapio Rautonen
8 | * %%
9 | * Permission is hereby granted, free of charge, to any person obtaining a copy
10 | * of this software and associated documentation files (the "Software"), to deal
11 | * in the Software without restriction, including without limitation the rights
12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | * copies of the Software, and to permit persons to whom the Software is
14 | * furnished to do so, subject to the following conditions:
15 | *
16 | * The above copyright notice and this permission notice shall be included in
17 | * all copies or substantial portions of the Software.
18 | *
19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 | * THE SOFTWARE.
26 | * %[license]
27 | */
28 |
29 | import ch.qos.logback.core.Appender;
30 |
31 | import java.util.concurrent.CountDownLatch;
32 | import java.util.concurrent.ExecutorService;
33 | import java.util.concurrent.Executors;
34 | import java.util.concurrent.ThreadFactory;
35 | import java.util.concurrent.TimeUnit;
36 | import java.util.concurrent.atomic.AtomicInteger;
37 |
38 | import static java.lang.String.format;
39 |
40 | public class AppenderExecutors {
41 |
42 | public static final int DEFAULT_THREAD_POOL_SIZE = 20;
43 | public static final int DEFAULT_MAX_FLUSH_TIME = 3000; // milliseconds
44 |
45 | public static ExecutorService newExecutor(Appender> appender, int threadPoolSize) {
46 | final String name = appender.getName();
47 | return Executors.newFixedThreadPool(threadPoolSize, new ThreadFactory() {
48 |
49 | private final AtomicInteger idx = new AtomicInteger(1);
50 |
51 | @Override
52 | public Thread newThread(Runnable r) {
53 | Thread thread = Executors.defaultThreadFactory().newThread(r);
54 | thread.setName(name + "-" + idx.getAndIncrement());
55 | thread.setDaemon(true);
56 | return thread;
57 | }
58 | });
59 | }
60 |
61 | public static void shutdown(Appender> appender, ExecutorService executor, long waitMillis) {
62 | executor.shutdown();
63 | boolean completed = awaitTermination(appender, executor, waitMillis);
64 | if (!completed) {
65 | appender.addWarn(format("Executor for %s did not shut down in %d milliseconds, " +
66 | "logging events might have been discarded",
67 | appender.getName(), waitMillis));
68 | }
69 | }
70 |
71 | public static void awaitLatch(Appender> appender, CountDownLatch latch, long waitMillis) {
72 | if (latch.getCount() > 0) {
73 | try {
74 | boolean completed = latch.await(waitMillis, TimeUnit.MILLISECONDS);
75 | if (!completed) {
76 | appender.addWarn(format("Appender '%s' did not complete sending event in %d milliseconds, " +
77 | "the event might have been lost",
78 | appender.getName(), waitMillis));
79 | }
80 | } catch (InterruptedException ex) {
81 | appender.addWarn(format("Appender '%s' was interrupted, " +
82 | "a logging event might have been lost or shutdown was initiated",
83 | appender.getName()));
84 | Thread.currentThread().interrupt();
85 | }
86 | }
87 | }
88 |
89 | private static boolean awaitTermination(Appender> appender, ExecutorService executor, long waitMillis) {
90 | long started = System.currentTimeMillis();
91 | try {
92 | return executor.awaitTermination(waitMillis, TimeUnit.MILLISECONDS);
93 | } catch (InterruptedException ie1) {
94 | // the worker loop is stopped by interrupt, but the remaining queue should still be handled
95 | long waited = System.currentTimeMillis() - started;
96 | if (waited < waitMillis) {
97 | try {
98 | return executor.awaitTermination(waitMillis - waited, TimeUnit.MILLISECONDS);
99 | } catch (InterruptedException ie2) {
100 | appender.addError(format("Shut down of executor for %s was interrupted",
101 | appender.getName()));
102 | }
103 | }
104 | Thread.currentThread().interrupt();
105 | }
106 | return false;
107 | }
108 |
109 | private AppenderExecutors() {
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/logback-ext-core/src/main/java/org/eluder/logback/ext/core/ByteArrayPayloadConverter.java:
--------------------------------------------------------------------------------
1 | package org.eluder.logback.ext.core;
2 |
3 | public class ByteArrayPayloadConverter implements PayloadConverter {
4 |
5 | @Override
6 | public byte[] convert(byte[] payload) {
7 | return payload;
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/logback-ext-core/src/main/java/org/eluder/logback/ext/core/CharacterEncoder.java:
--------------------------------------------------------------------------------
1 | package org.eluder.logback.ext.core;
2 |
3 | /*
4 | * #[license]
5 | * logback-ext-core
6 | * %%
7 | * Copyright (C) 2014 - 2015 Tapio Rautonen
8 | * %%
9 | * Permission is hereby granted, free of charge, to any person obtaining a copy
10 | * of this software and associated documentation files (the "Software"), to deal
11 | * in the Software without restriction, including without limitation the rights
12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | * copies of the Software, and to permit persons to whom the Software is
14 | * furnished to do so, subject to the following conditions:
15 | *
16 | * The above copyright notice and this permission notice shall be included in
17 | * all copies or substantial portions of the Software.
18 | *
19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 | * THE SOFTWARE.
26 | * %[license]
27 | */
28 |
29 | import ch.qos.logback.core.encoder.Encoder;
30 |
31 | import java.nio.charset.Charset;
32 |
33 | public interface CharacterEncoder extends Encoder {
34 |
35 | void setCharset(Charset charset);
36 |
37 | Charset getCharset();
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/logback-ext-core/src/main/java/org/eluder/logback/ext/core/CommonEventAttributes.java:
--------------------------------------------------------------------------------
1 | package org.eluder.logback.ext.core;
2 |
3 | public interface CommonEventAttributes {
4 |
5 | String getThreadName();
6 |
7 | long getTimeStamp();
8 |
9 | }
10 |
--------------------------------------------------------------------------------
/logback-ext-core/src/main/java/org/eluder/logback/ext/core/ContextAwareExecutorService.java:
--------------------------------------------------------------------------------
1 | package org.eluder.logback.ext.core;
2 |
3 | /*
4 | * #[license]
5 | * logback-ext-core
6 | * %%
7 | * Copyright (C) 2014 - 2015 Tapio Rautonen
8 | * %%
9 | * Permission is hereby granted, free of charge, to any person obtaining a copy
10 | * of this software and associated documentation files (the "Software"), to deal
11 | * in the Software without restriction, including without limitation the rights
12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | * copies of the Software, and to permit persons to whom the Software is
14 | * furnished to do so, subject to the following conditions:
15 | *
16 | * The above copyright notice and this permission notice shall be included in
17 | * all copies or substantial portions of the Software.
18 | *
19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 | * THE SOFTWARE.
26 | * %[license]
27 | */
28 |
29 | import ch.qos.logback.core.spi.ContextAware;
30 |
31 | import java.util.Collection;
32 | import java.util.List;
33 | import java.util.concurrent.Callable;
34 | import java.util.concurrent.ExecutionException;
35 | import java.util.concurrent.ExecutorService;
36 | import java.util.concurrent.Future;
37 | import java.util.concurrent.TimeUnit;
38 | import java.util.concurrent.TimeoutException;
39 |
40 | public class ContextAwareExecutorService implements ExecutorService {
41 |
42 | private final ContextAware contextAware;
43 |
44 | public ContextAwareExecutorService(ContextAware contextAware) {
45 | this.contextAware = contextAware;
46 | }
47 |
48 | @Override
49 | public void shutdown() {
50 | contextAware.getContext().getExecutorService().shutdown();
51 | }
52 |
53 | @Override
54 | public List shutdownNow() {
55 | return contextAware.getContext().getExecutorService().shutdownNow();
56 | }
57 |
58 | @Override
59 | public boolean isShutdown() {
60 | return contextAware.getContext().getExecutorService().isShutdown();
61 | }
62 |
63 | @Override
64 | public boolean isTerminated() {
65 | return contextAware.getContext().getExecutorService().isTerminated();
66 | }
67 |
68 | @Override
69 | public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
70 | return contextAware.getContext().getExecutorService().awaitTermination(timeout, unit);
71 | }
72 |
73 | @Override
74 | public Future submit(Callable task) {
75 | return contextAware.getContext().getExecutorService().submit(task);
76 | }
77 |
78 | @Override
79 | public Future submit(Runnable task, T result) {
80 | return contextAware.getContext().getExecutorService().submit(task, result);
81 | }
82 |
83 | @Override
84 | public Future> submit(Runnable task) {
85 | return contextAware.getContext().getExecutorService().submit(task);
86 | }
87 |
88 | @Override
89 | public List> invokeAll(Collection extends Callable> tasks) throws InterruptedException {
90 | return contextAware.getContext().getExecutorService().invokeAll(tasks);
91 | }
92 |
93 | @Override
94 | public List> invokeAll(Collection extends Callable> tasks, long timeout, TimeUnit unit) throws InterruptedException {
95 | return contextAware.getContext().getExecutorService().invokeAll(tasks, timeout, unit);
96 | }
97 |
98 | @Override
99 | public T invokeAny(Collection extends Callable> tasks) throws InterruptedException, ExecutionException {
100 | return contextAware.getContext().getExecutorService().invokeAny(tasks);
101 | }
102 |
103 | @Override
104 | public T invokeAny(Collection extends Callable> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
105 | return contextAware.getContext().getExecutorService().invokeAny(tasks, timeout, unit);
106 | }
107 |
108 | @Override
109 | public void execute(Runnable command) {
110 | contextAware.getContext().getExecutorService().execute(command);
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/logback-ext-core/src/main/java/org/eluder/logback/ext/core/EncodingStringAppender.java:
--------------------------------------------------------------------------------
1 | package org.eluder.logback.ext.core;
2 |
3 | /*
4 | * #[license]
5 | * logback-ext-core
6 | * %%
7 | * Copyright (C) 2014 - 2015 Tapio Rautonen
8 | * %%
9 | * Permission is hereby granted, free of charge, to any person obtaining a copy
10 | * of this software and associated documentation files (the "Software"), to deal
11 | * in the Software without restriction, including without limitation the rights
12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | * copies of the Software, and to permit persons to whom the Software is
14 | * furnished to do so, subject to the following conditions:
15 | *
16 | * The above copyright notice and this permission notice shall be included in
17 | * all copies or substantial portions of the Software.
18 | *
19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 | * THE SOFTWARE.
26 | * %[license]
27 | */
28 |
29 | import ch.qos.logback.core.Context;
30 | import ch.qos.logback.core.Layout;
31 | import ch.qos.logback.core.UnsynchronizedAppenderBase;
32 | import ch.qos.logback.core.encoder.Encoder;
33 | import ch.qos.logback.core.encoder.LayoutWrappingEncoder;
34 | import ch.qos.logback.core.spi.DeferredProcessingAware;
35 |
36 | import java.io.ByteArrayOutputStream;
37 | import java.io.IOException;
38 | import java.nio.charset.Charset;
39 | import java.util.concurrent.locks.ReentrantLock;
40 |
41 | import static java.lang.String.format;
42 |
43 | public abstract class EncodingStringAppender extends UnsynchronizedAppenderBase {
44 |
45 | protected final ReentrantLock lock = new ReentrantLock(true);
46 |
47 | private Charset charset = Charset.forName("UTF-8");
48 | private boolean binary = false;
49 | private Encoder encoder;
50 | private PayloadConverter converter;
51 |
52 | public final void setCharset(Charset charset) {
53 | if (encoder instanceof LayoutWrappingEncoder) {
54 | ((LayoutWrappingEncoder>) encoder).setCharset(charset);
55 | } else if (encoder instanceof CharacterEncoder) {
56 | ((CharacterEncoder>) encoder).setCharset(charset);
57 | }
58 | this.charset = charset;
59 | }
60 |
61 | public final void setBinary(boolean binary) {
62 | if (binary) {
63 | addInfo(format("Appender '%s' is set to binary mode, events are converted to Base64 strings", getName()));
64 | }
65 | this.binary = binary;
66 | }
67 |
68 | public final void setEncoder(Encoder encoder) {
69 | this.encoder = encoder;
70 | setContext(context);
71 | setCharset(charset);
72 | }
73 |
74 | public final void setLayout(Layout layout) {
75 | LayoutWrappingEncoder enc = new LayoutWrappingEncoder<>();
76 | enc.setLayout(layout);
77 | setEncoder(enc);
78 | }
79 |
80 | public final void setConverter(PayloadConverter converter) {
81 | this.converter = converter;
82 | }
83 |
84 | @Override
85 | public void setContext(Context context) {
86 | if (encoder != null) {
87 | encoder.setContext(context);
88 | }
89 | super.setContext(context);
90 | }
91 |
92 | protected final Charset getCharset() {
93 | return charset;
94 | }
95 |
96 | protected final boolean isBinary() {
97 | return binary;
98 | }
99 |
100 | protected final Encoder getEncoder() {
101 | return encoder;
102 | }
103 |
104 | @Override
105 | public void start() {
106 | if (encoder == null) {
107 | addError(format("Encoder not set for appender '%s'", getName()));
108 | return;
109 | }
110 | if (converter == null) {
111 | addError(format("Converter not set for appender '%s'", getName()));
112 | return;
113 | }
114 | lock.lock();
115 | try {
116 | encoder.start();
117 | super.start();
118 | } finally {
119 | lock.unlock();
120 | }
121 | }
122 |
123 | @Override
124 | public void stop() {
125 | lock.lock();
126 | try {
127 | super.stop();
128 | if (encoder != null) {
129 | encoder.stop();
130 | }
131 | } finally {
132 | lock.unlock();
133 | }
134 | }
135 |
136 | @Override
137 | protected void append(E event) {
138 | ByteArrayOutputStream stream = new ByteArrayOutputStream();
139 | encode(event, stream);
140 | doHandle(event, convert(stream.toByteArray()));
141 | }
142 |
143 | private void encode(E event, ByteArrayOutputStream stream) {
144 | lock.lock();
145 | try {
146 | encoderInit(stream);
147 | try {
148 | doEncode(event);
149 | } finally {
150 | encoderClose();
151 | }
152 | } finally {
153 | lock.unlock();
154 | }
155 | }
156 |
157 | protected abstract void handle(E event, P encoded) throws Exception;
158 |
159 | protected P convert(byte[] payload) {
160 | return converter.convert(payload);
161 | }
162 |
163 | protected void doHandle(E event, P encoded) {
164 | try {
165 | if (encoded != null) {
166 | handle(event, encoded);
167 | }
168 | } catch (Exception ex) {
169 | this.started = false;
170 | addError(format("Failed to handle logging event for '%s'", getName()), ex);
171 | }
172 | }
173 |
174 | protected void doEncode(E event) {
175 | try {
176 | encoder.doEncode(event);
177 | } catch (IOException ex) {
178 | this.started = false;
179 | addError(format("Failed to encode logging event for appender '%s'", getName()), ex);
180 | }
181 | }
182 |
183 | protected void encoderInit(ByteArrayOutputStream stream) {
184 | try {
185 | encoder.init(stream);
186 | } catch (IOException ex) {
187 | this.started = false;
188 | addError(format("Failed to initialize encoder for appender '%s'", getName()), ex);
189 | }
190 | }
191 |
192 | protected void encoderClose() {
193 | try {
194 | encoder.close();
195 | } catch (Exception ex) {
196 | this.started = false;
197 | addError(format("Failed to close encoder for appender '%s'", getName()), ex);
198 | }
199 | }
200 | }
201 |
--------------------------------------------------------------------------------
/logback-ext-core/src/main/java/org/eluder/logback/ext/core/FieldNames.java:
--------------------------------------------------------------------------------
1 | package org.eluder.logback.ext.core;
2 |
3 | /*
4 | * #[license]
5 | * logback-ext-core
6 | * %%
7 | * Copyright (C) 2014 - 2015 Tapio Rautonen
8 | * %%
9 | * Permission is hereby granted, free of charge, to any person obtaining a copy
10 | * of this software and associated documentation files (the "Software"), to deal
11 | * in the Software without restriction, including without limitation the rights
12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | * copies of the Software, and to permit persons to whom the Software is
14 | * furnished to do so, subject to the following conditions:
15 | *
16 | * The above copyright notice and this permission notice shall be included in
17 | * all copies or substantial portions of the Software.
18 | *
19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 | * THE SOFTWARE.
26 | * %[license]
27 | */
28 |
29 | public class FieldNames {
30 |
31 | public static final String IGNORE_NAME = "[ignore]";
32 |
33 | private String timeStamp = "timeStamp";
34 | private String level = "level";
35 | private String levelValue = "levelValue";
36 | private String threadName = "thread";
37 | private String loggerName = "logger";
38 | private String message = "message";
39 | private String formattedMessage = "formattedMessage";
40 | private String stackTrace = "stackTrace";
41 | private String callerData = "caller";
42 | private String callerClass = "class";
43 | private String callerMethod = "method";
44 | private String callerFile = "file";
45 | private String callerLine = "line";
46 | private String mdc = "mdc";
47 | private String marker = "marker";
48 |
49 | public String getTimeStamp() {
50 | return timeStamp;
51 | }
52 |
53 | public void setTimeStamp(String timeStamp) {
54 | this.timeStamp = timeStamp;
55 | }
56 |
57 | public String getLevel() {
58 | return level;
59 | }
60 |
61 | public void setLevel(String level) {
62 | this.level = level;
63 | }
64 |
65 | public String getLevelValue() {
66 | return levelValue;
67 | }
68 |
69 | public void setLevelValue(String levelValue) {
70 | this.levelValue = levelValue;
71 | }
72 |
73 | public String getThreadName() {
74 | return threadName;
75 | }
76 |
77 | public void setThreadName(String threadName) {
78 | this.threadName = threadName;
79 | }
80 |
81 | public String getLoggerName() {
82 | return loggerName;
83 | }
84 |
85 | public void setLoggerName(String loggerName) {
86 | this.loggerName = loggerName;
87 | }
88 |
89 | public String getMessage() {
90 | return message;
91 | }
92 |
93 | public void setMessage(String message) {
94 | this.message = message;
95 | }
96 |
97 | public String getFormattedMessage() {
98 | return formattedMessage;
99 | }
100 |
101 | public void setFormattedMessage(String formattedMessage) {
102 | this.formattedMessage = formattedMessage;
103 | }
104 |
105 | public String getStackTrace() {
106 | return stackTrace;
107 | }
108 |
109 | public void setStackTrace(String stackTrace) {
110 | this.stackTrace = stackTrace;
111 | }
112 |
113 | public String getCallerData() {
114 | return callerData;
115 | }
116 |
117 | public void setCallerData(String callerData) {
118 | this.callerData = callerData;
119 | }
120 |
121 | public String getCallerClass() {
122 | return callerClass;
123 | }
124 |
125 | public void setCallerClass(String callerClass) {
126 | this.callerClass = callerClass;
127 | }
128 |
129 | public String getCallerMethod() {
130 | return callerMethod;
131 | }
132 |
133 | public void setCallerMethod(String callerMethod) {
134 | this.callerMethod = callerMethod;
135 | }
136 |
137 | public String getCallerFile() {
138 | return callerFile;
139 | }
140 |
141 | public void setCallerFile(String callerFile) {
142 | this.callerFile = callerFile;
143 | }
144 |
145 | public String getCallerLine() {
146 | return callerLine;
147 | }
148 |
149 | public void setCallerLine(String callerLine) {
150 | this.callerLine = callerLine;
151 | }
152 |
153 | public String getMdc() {
154 | return mdc;
155 | }
156 |
157 | public void setMdc(String mdc) {
158 | this.mdc = mdc;
159 | }
160 |
161 | public String getMarker() {
162 | return marker;
163 | }
164 |
165 | public void setMarker(String marker) {
166 | this.marker = marker;
167 | }
168 | }
169 |
--------------------------------------------------------------------------------
/logback-ext-core/src/main/java/org/eluder/logback/ext/core/PayloadConverter.java:
--------------------------------------------------------------------------------
1 | package org.eluder.logback.ext.core;
2 |
3 | public interface PayloadConverter {
4 |
5 | P convert(byte[] payload);
6 |
7 | }
8 |
--------------------------------------------------------------------------------
/logback-ext-core/src/main/java/org/eluder/logback/ext/core/StringPayloadConverter.java:
--------------------------------------------------------------------------------
1 | package org.eluder.logback.ext.core;
2 |
3 | import com.google.common.io.BaseEncoding;
4 |
5 | import java.nio.charset.Charset;
6 |
7 | public class StringPayloadConverter implements PayloadConverter {
8 |
9 | private final Charset charset;
10 | private final boolean binary;
11 |
12 | public StringPayloadConverter(Charset charset, boolean binary) {
13 | this.charset = charset;
14 | this.binary = binary;
15 | }
16 |
17 | @Override
18 | public String convert(byte[] payload) {
19 | if (payload == null) {
20 | return null;
21 | } else if (binary) {
22 | return BaseEncoding.base64().encode(payload);
23 | } else {
24 | return new String(payload, charset);
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/logback-ext-dynamodb-appender/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | logback-ext
8 | org.eluder.logback
9 | 1.0-SNAPSHOT
10 |
11 |
12 | logback-ext-dynamodb-appender
13 |
14 |
15 |
16 | ch.qos.logback
17 | logback-core
18 |
19 |
20 | ch.qos.logback
21 | logback-classic
22 |
23 |
24 | org.eluder.logback
25 | logback-ext-core
26 |
27 |
28 | org.eluder.logback
29 | logback-ext-aws-core
30 |
31 |
32 | org.eluder.logback
33 | logback-ext-jackson
34 |
35 |
36 | com.amazonaws
37 | aws-java-sdk-core
38 | ${aws.version}
39 |
40 |
41 | com.amazonaws
42 | aws-java-sdk-dynamodb
43 | ${aws.version}
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/logback-ext-dynamodb-appender/src/main/java/org/eluder/logback/ext/dynamodb/appender/AsyncDynamoDbAppender.java:
--------------------------------------------------------------------------------
1 | package org.eluder.logback.ext.dynamodb.appender;
2 |
3 | /*
4 | * #[license]
5 | * logback-ext-dynamodb-appender
6 | * %%
7 | * Copyright (C) 2014 - 2015 Tapio Rautonen
8 | * %%
9 | * Permission is hereby granted, free of charge, to any person obtaining a copy
10 | * of this software and associated documentation files (the "Software"), to deal
11 | * in the Software without restriction, including without limitation the rights
12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | * copies of the Software, and to permit persons to whom the Software is
14 | * furnished to do so, subject to the following conditions:
15 | *
16 | * The above copyright notice and this permission notice shall be included in
17 | * all copies or substantial portions of the Software.
18 | *
19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 | * THE SOFTWARE.
26 | * %[license]
27 | */
28 |
29 | import ch.qos.logback.classic.AsyncAppender;
30 | import ch.qos.logback.classic.spi.ILoggingEvent;
31 | import ch.qos.logback.core.Context;
32 | import ch.qos.logback.core.Layout;
33 | import ch.qos.logback.core.encoder.Encoder;
34 |
35 | import java.nio.charset.Charset;
36 |
37 | public class AsyncDynamoDbAppender extends AsyncAppender {
38 |
39 | private final DynamoDbAppender appender;
40 |
41 | public AsyncDynamoDbAppender() {
42 | this(new DynamoDbAppender());
43 | }
44 |
45 | protected AsyncDynamoDbAppender(DynamoDbAppender appender) {
46 | appender.setAsyncParent(true);
47 | addAppender(appender);
48 | this.appender = appender;
49 | }
50 |
51 | public void setAccessKey(String accessKey) {
52 | appender.setAccessKey(accessKey);
53 | }
54 |
55 | public void setSecretKey(String secretKey) {
56 | appender.setSecretKey(secretKey);
57 | }
58 |
59 | public void setMaxPayloadSize(int maxPayloadSize) {
60 | appender.setMaxPayloadSize(maxPayloadSize);
61 | }
62 |
63 | public void setRegion(String region) {
64 | appender.setRegion(region);
65 | }
66 |
67 | public void setTable(String table) {
68 | appender.setTable(table);
69 | }
70 |
71 | public void setPrimaryKey(String primaryKey) {
72 | appender.setPrimaryKey(primaryKey);
73 | }
74 |
75 | public final void setThreadPoolSize(int threadPoolSize) {
76 | appender.setThreadPoolSize(threadPoolSize);
77 | }
78 |
79 | @Override
80 | public final void setMaxFlushTime(int maxFlushTime) {
81 | appender.setMaxFlushTime(maxFlushTime);
82 | // add an extra 100 millis to wait for the internal event queue handling
83 | super.setMaxFlushTime(maxFlushTime + 100);
84 | }
85 |
86 | public void setCharset(Charset charset) {
87 | appender.setCharset(charset);
88 | }
89 |
90 | public void setEncoder(Encoder encoder) {
91 | appender.setEncoder(encoder);
92 | }
93 |
94 | public void setLayout(Layout layout) {
95 | appender.setLayout(layout);
96 | }
97 |
98 | public void setBinary(boolean binary) {
99 | appender.setBinary(binary);
100 | }
101 |
102 | @Override
103 | public void setName(String name) {
104 | appender.setName(name);
105 | super.setName(name);
106 | }
107 |
108 | @Override
109 | public void setContext(Context context) {
110 | appender.setContext(context);
111 | super.setContext(context);
112 | }
113 |
114 | @Override
115 | public void start() {
116 | appender.start();
117 | super.start();
118 | }
119 |
120 | }
121 |
--------------------------------------------------------------------------------
/logback-ext-dynamodb-appender/src/main/java/org/eluder/logback/ext/dynamodb/appender/CapitalizingFieldNames.java:
--------------------------------------------------------------------------------
1 | package org.eluder.logback.ext.dynamodb.appender;
2 |
3 | /*
4 | * #[license]
5 | * logback-ext-dynamodb-appender
6 | * %%
7 | * Copyright (C) 2014 - 2015 Tapio Rautonen
8 | * %%
9 | * Permission is hereby granted, free of charge, to any person obtaining a copy
10 | * of this software and associated documentation files (the "Software"), to deal
11 | * in the Software without restriction, including without limitation the rights
12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | * copies of the Software, and to permit persons to whom the Software is
14 | * furnished to do so, subject to the following conditions:
15 | *
16 | * The above copyright notice and this permission notice shall be included in
17 | * all copies or substantial portions of the Software.
18 | *
19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 | * THE SOFTWARE.
26 | * %[license]
27 | */
28 |
29 | import org.eluder.logback.ext.core.FieldNames;
30 |
31 | public class CapitalizingFieldNames extends FieldNames {
32 |
33 | public CapitalizingFieldNames() {
34 | setTimeStamp(getTimeStamp());
35 | setLevel(getLevel());
36 | setLevelValue(getLevelValue());
37 | setThreadName(getThreadName());
38 | setLoggerName(getLoggerName());
39 | setMessage(getMessage());
40 | setFormattedMessage(getFormattedMessage());
41 | setStackTrace(getStackTrace());
42 | setCallerData(getCallerData());
43 | setCallerClass(getCallerClass());
44 | setCallerMethod(getCallerMethod());
45 | setCallerFile(getCallerFile());
46 | setCallerLine(getCallerLine());
47 | setMdc(getMdc());
48 | setMarker(getMarker());
49 | }
50 |
51 | @Override
52 | public void setTimeStamp(String timeStamp) {
53 | super.setTimeStamp(capitalize(timeStamp));
54 | }
55 |
56 | @Override
57 | public void setLevel(String level) {
58 | super.setLevel(capitalize(level));
59 | }
60 |
61 | @Override
62 | public void setLevelValue(String levelValue) {
63 | super.setLevelValue(capitalize(levelValue));
64 | }
65 |
66 | @Override
67 | public void setThreadName(String threadName) {
68 | super.setThreadName(capitalize(threadName));
69 | }
70 |
71 | @Override
72 | public void setLoggerName(String loggerName) {
73 | super.setLoggerName(capitalize(loggerName));
74 | }
75 |
76 | @Override
77 | public void setMessage(String message) {
78 | super.setMessage(capitalize(message));
79 | }
80 |
81 | @Override
82 | public void setFormattedMessage(String formattedMessage) {
83 | super.setFormattedMessage(capitalize(formattedMessage));
84 | }
85 |
86 | @Override
87 | public void setStackTrace(String stackTrace) {
88 | super.setStackTrace(capitalize(stackTrace));
89 | }
90 |
91 | @Override
92 | public void setCallerData(String callerData) {
93 | super.setCallerData(capitalize(callerData));
94 | }
95 |
96 | @Override
97 | public void setCallerClass(String callerClass) {
98 | super.setCallerClass(capitalize(callerClass));
99 | }
100 |
101 | @Override
102 | public void setCallerMethod(String callerMethod) {
103 | super.setCallerMethod(capitalize(callerMethod));
104 | }
105 |
106 | @Override
107 | public void setCallerFile(String callerFile) {
108 | super.setCallerFile(capitalize(callerFile));
109 | }
110 |
111 | @Override
112 | public void setCallerLine(String callerLine) {
113 | super.setCallerLine(capitalize(callerLine));
114 | }
115 |
116 | @Override
117 | public void setMdc(String mdc) {
118 | super.setMdc(capitalize(mdc));
119 | }
120 |
121 | @Override
122 | public void setMarker(String marker) {
123 | super.setMarker(capitalize(marker));
124 | }
125 |
126 | private String capitalize(String value) {
127 | if (value == null || value.isEmpty() || FieldNames.IGNORE_NAME.equals(value)) {
128 | return value;
129 | } else {
130 | return value.substring(0, 1).toUpperCase() + value.substring(1);
131 | }
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/logback-ext-dynamodb-appender/src/main/java/org/eluder/logback/ext/dynamodb/appender/DynamoDbAppender.java:
--------------------------------------------------------------------------------
1 | package org.eluder.logback.ext.dynamodb.appender;
2 |
3 | /*
4 | * #[license]
5 | * logback-ext-dynamodb-appender
6 | * %%
7 | * Copyright (C) 2014 - 2015 Tapio Rautonen
8 | * %%
9 | * Permission is hereby granted, free of charge, to any person obtaining a copy
10 | * of this software and associated documentation files (the "Software"), to deal
11 | * in the Software without restriction, including without limitation the rights
12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | * copies of the Software, and to permit persons to whom the Software is
14 | * furnished to do so, subject to the following conditions:
15 | *
16 | * The above copyright notice and this permission notice shall be included in
17 | * all copies or substantial portions of the Software.
18 | *
19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 | * THE SOFTWARE.
26 | * %[license]
27 | */
28 |
29 | import ch.qos.logback.classic.spi.ILoggingEvent;
30 | import ch.qos.logback.core.filter.Filter;
31 | import com.amazonaws.regions.RegionUtils;
32 | import com.amazonaws.services.dynamodbv2.AmazonDynamoDBAsyncClient;
33 | import com.amazonaws.services.dynamodbv2.document.Item;
34 | import com.amazonaws.services.dynamodbv2.document.PrimaryKey;
35 | import com.amazonaws.services.dynamodbv2.document.internal.InternalUtils;
36 | import com.amazonaws.services.dynamodbv2.model.AttributeValue;
37 | import com.amazonaws.services.dynamodbv2.model.PutItemRequest;
38 | import com.amazonaws.services.dynamodbv2.model.PutItemResult;
39 | import org.eluder.logback.ext.aws.core.AbstractAwsEncodingStringAppender;
40 | import org.eluder.logback.ext.aws.core.AwsSupport;
41 | import org.eluder.logback.ext.core.AppenderExecutors;
42 | import org.eluder.logback.ext.aws.core.LoggingEventHandler;
43 | import org.eluder.logback.ext.core.StringPayloadConverter;
44 | import org.eluder.logback.ext.jackson.JacksonEncoder;
45 |
46 | import java.util.Map;
47 | import java.util.UUID;
48 | import java.util.concurrent.CountDownLatch;
49 |
50 | import static java.lang.String.format;
51 |
52 | public class DynamoDbAppender extends AbstractAwsEncodingStringAppender {
53 |
54 | private static final String TIMESTAMP_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSZ";
55 | private static final String DEFAULT_PRIMARY_KEY = "Id";
56 | private static final int DEFAULT_MAX_PAYLOAD_SIZE = 384;
57 |
58 | private String region;
59 | private String table;
60 | private String primaryKey = DEFAULT_PRIMARY_KEY;
61 |
62 | private AmazonDynamoDBAsyncClient dynamoDb;
63 |
64 | public DynamoDbAppender() {
65 | super();
66 | setMaxPayloadSize(DEFAULT_MAX_PAYLOAD_SIZE);
67 | }
68 |
69 | protected DynamoDbAppender(AwsSupport awsSupport, Filter sdkLoggingFilter) {
70 | super(awsSupport, sdkLoggingFilter);
71 | setMaxPayloadSize(DEFAULT_MAX_PAYLOAD_SIZE);
72 | }
73 |
74 | public void setRegion(String region) {
75 | this.region = region;
76 | }
77 |
78 | public void setTable(String table) {
79 | this.table = table;
80 | }
81 |
82 | public void setPrimaryKey(String primaryKey) {
83 | this.primaryKey = primaryKey;
84 | }
85 |
86 | @Override
87 | public void start() {
88 | if (getEncoder() == null) {
89 | JacksonEncoder encoder = new JacksonEncoder();
90 | encoder.setFieldNames(new CapitalizingFieldNames());
91 | encoder.setTimeStampFormat(TIMESTAMP_FORMAT);
92 | setEncoder(encoder);
93 | }
94 | setConverter(new StringPayloadConverter(getCharset(), isBinary()));
95 | super.start();
96 | }
97 |
98 | @Override
99 | protected void doStart() {
100 | dynamoDb = new AmazonDynamoDBAsyncClient(
101 | getCredentials(),
102 | getClientConfiguration(),
103 | AppenderExecutors.newExecutor(this, getThreadPoolSize())
104 | );
105 | dynamoDb.setRegion(RegionUtils.getRegion(region));
106 | }
107 |
108 | @Override
109 | protected void doStop() {
110 | if (dynamoDb != null) {
111 | AppenderExecutors.shutdown(this, dynamoDb.getExecutorService(), getMaxFlushTime());
112 | dynamoDb.shutdown();
113 | dynamoDb = null;
114 | }
115 | }
116 |
117 | @Override
118 | protected void handle(final ILoggingEvent event, final String encoded) throws Exception {
119 | Item item = Item.fromJSON(encoded).withPrimaryKey(createEventId(event));
120 | Map attributes = InternalUtils.toAttributeValues(item);
121 | PutItemRequest request = new PutItemRequest(table, attributes);
122 | String errorMessage = format("Appender '%s' failed to send logging event '%s' to DynamoDB table '%s'", getName(), event, table);
123 | CountDownLatch latch = new CountDownLatch(isAsyncParent() ? 0 : 1);
124 | dynamoDb.putItemAsync(request, new LoggingEventHandler(this, latch, errorMessage));
125 | AppenderExecutors.awaitLatch(this, latch, getMaxFlushTime());
126 | }
127 |
128 | protected PrimaryKey createEventId(ILoggingEvent event) {
129 | return new PrimaryKey(primaryKey, UUID.randomUUID().toString());
130 | }
131 |
132 | }
133 |
--------------------------------------------------------------------------------
/logback-ext-jackson/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 |
8 | logback-ext
9 | org.eluder.logback
10 | 1.0-SNAPSHOT
11 |
12 |
13 | logback-ext-jackson
14 |
15 |
16 |
17 | ch.qos.logback
18 | logback-core
19 |
20 |
21 | ch.qos.logback
22 | logback-classic
23 |
24 |
25 | org.eluder.logback
26 | logback-ext-core
27 |
28 |
29 | com.fasterxml.jackson.core
30 | jackson-core
31 | ${jackson.version}
32 |
33 |
34 | com.fasterxml.jackson.core
35 | jackson-databind
36 | ${jackson.version}
37 |
38 |
39 | junit
40 | junit
41 | test
42 |
43 |
44 | org.assertj
45 | assertj-core
46 | test
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/logback-ext-jackson/src/main/java/org/eluder/logback/ext/jackson/JacksonEncoder.java:
--------------------------------------------------------------------------------
1 | package org.eluder.logback.ext.jackson;
2 |
3 | /*
4 | * #[license]
5 | * logback-ext-jackson
6 | * %%
7 | * Copyright (C) 2014 - 2015 Tapio Rautonen
8 | * %%
9 | * Permission is hereby granted, free of charge, to any person obtaining a copy
10 | * of this software and associated documentation files (the "Software"), to deal
11 | * in the Software without restriction, including without limitation the rights
12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | * copies of the Software, and to permit persons to whom the Software is
14 | * furnished to do so, subject to the following conditions:
15 | *
16 | * The above copyright notice and this permission notice shall be included in
17 | * all copies or substantial portions of the Software.
18 | *
19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 | * THE SOFTWARE.
26 | * %[license]
27 | */
28 |
29 | import ch.qos.logback.classic.pattern.ThrowableProxyConverter;
30 | import ch.qos.logback.classic.spi.ILoggingEvent;
31 | import ch.qos.logback.core.Context;
32 | import ch.qos.logback.core.spi.ContextAwareBase;
33 | import com.fasterxml.jackson.core.JsonGenerator;
34 | import com.fasterxml.jackson.databind.ObjectMapper;
35 | import org.eluder.logback.ext.core.CharacterEncoder;
36 | import org.eluder.logback.ext.core.FieldNames;
37 | import org.slf4j.Marker;
38 |
39 | import java.io.IOException;
40 | import java.io.OutputStream;
41 | import java.nio.charset.Charset;
42 | import java.text.SimpleDateFormat;
43 | import java.util.Date;
44 | import java.util.Iterator;
45 | import java.util.Map;
46 |
47 | public class JacksonEncoder extends ContextAwareBase implements CharacterEncoder {
48 |
49 | private final ObjectMapper mapper = new ObjectMapper().configure(JsonGenerator.Feature.ESCAPE_NON_ASCII, true);
50 | private final ThrowableProxyConverter throwableProxyConverter = new ThrowableProxyConverter();
51 |
52 | private Charset charset = Charset.forName("UTF-8");
53 | private FieldNames fieldNames = new FieldNames();
54 | private String timeStampFormat;
55 | private boolean newline;
56 |
57 | private boolean started;
58 | private JsonWriter writer;
59 |
60 | @Override
61 | public final void setCharset(Charset charset) {
62 | this.charset = charset;
63 | }
64 |
65 | @Override
66 | public final Charset getCharset() {
67 | return charset;
68 | }
69 |
70 | public final void setFieldNames(FieldNames fieldNames) {
71 | this.fieldNames = fieldNames;
72 | }
73 |
74 | public final FieldNames getFieldNames() {
75 | return fieldNames;
76 | }
77 |
78 | public final void setTimeStampFormat(String timeStampFormat) {
79 | this.timeStampFormat = timeStampFormat;
80 | }
81 |
82 | public final String getTimeStampFormat() {
83 | return timeStampFormat;
84 | }
85 |
86 | public final void setNewline(boolean newline) {
87 | this.newline = newline;
88 | }
89 |
90 | public final boolean isNewline() {
91 | return newline;
92 | }
93 |
94 | @Override
95 | public void setContext(Context context) {
96 | throwableProxyConverter.setContext(context);
97 | super.setContext(context);
98 | }
99 |
100 | @Override
101 | public boolean isStarted() {
102 | return started;
103 | }
104 |
105 | @Override
106 | public void start() {
107 | throwableProxyConverter.start();
108 | started = true;
109 | }
110 |
111 | @Override
112 | public void stop() {
113 | started = false;
114 | throwableProxyConverter.stop();
115 | }
116 |
117 | @Override
118 | public void close() throws IOException {
119 | if (writer != null) {
120 | writer.close();
121 | writer = null;
122 | }
123 | }
124 |
125 | @Override
126 | public void init(OutputStream os) throws IOException {
127 | writer = new JsonWriter(os, charset, getMapper());
128 | }
129 |
130 | @Override
131 | public void doEncode(ILoggingEvent event) throws IOException {
132 | JsonWriter.ObjectWriter ow = writer.writeObject();
133 | writeTimeStamp(ow, event);
134 | writeLogger(ow, event);
135 | writeMessage(ow, event);
136 | writeStackTrace(ow, event);
137 | writeCallerData(ow, event);
138 | writeMarker(ow, event);
139 | writeMdc(ow, event);
140 | JsonWriter w = ow.done();
141 | if (newline) {
142 | w.newline();
143 | }
144 | w.flush();
145 | }
146 |
147 | protected ObjectMapper getMapper() {
148 | return mapper;
149 | }
150 |
151 | protected void writeTimeStamp(JsonWriter.ObjectWriter writer, ILoggingEvent event) throws IOException {
152 | if (timeStampFormat != null) {
153 | String timeStamp = new SimpleDateFormat(timeStampFormat).format(new Date(event.getTimeStamp()));
154 | writer.writeStringField(fieldNames.getTimeStamp(), timeStamp, isActive(fieldNames.getTimeStamp()));
155 | } else {
156 | writer.writeNumberField(fieldNames.getTimeStamp(), event.getTimeStamp(), isActive(fieldNames.getTimeStamp()));
157 | }
158 | }
159 |
160 | protected void writeLogger(JsonWriter.ObjectWriter writer, ILoggingEvent event) throws IOException {
161 | writer.writeStringField(fieldNames.getLevel(), event.getLevel().toString(), isActive(fieldNames.getLevel()));
162 | writer.writeNumberField(fieldNames.getLevelValue(), event.getLevel().toInt(), isActive(fieldNames.getLevelValue()));
163 | writer.writeStringField(fieldNames.getThreadName(), event.getThreadName(), isActive(fieldNames.getThreadName()));
164 | writer.writeStringField(fieldNames.getLoggerName(), event.getLoggerName(), isActive(fieldNames.getLoggerName()));
165 | }
166 |
167 | protected void writeMessage(JsonWriter.ObjectWriter writer, ILoggingEvent event) throws IOException {
168 | writer.writeStringField(fieldNames.getMessage(), event.getMessage(), isActive(fieldNames.getMessage()));
169 | writer.writeStringField(fieldNames.getFormattedMessage(), event.getFormattedMessage(), isActive(fieldNames.getFormattedMessage()));
170 | }
171 |
172 | protected void writeStackTrace(JsonWriter.ObjectWriter writer, ILoggingEvent event) throws IOException {
173 | String stackTrace = throwableProxyConverter.convert(event);
174 | if (stackTrace != null && !stackTrace.isEmpty()) {
175 | writer.writeStringField(fieldNames.getStackTrace(), stackTrace, isActive(fieldNames.getStackTrace()));
176 | }
177 | }
178 |
179 | protected void writeCallerData(JsonWriter.ObjectWriter writer, ILoggingEvent event) throws IOException {
180 | if (event.hasCallerData()) {
181 | StackTraceElement callerData = event.getCallerData()[0];
182 | JsonWriter.ObjectWriter> ow =
183 | writer.writeObject(fieldNames.getCallerData(), isActive(fieldNames.getCallerData()));
184 | ow.writeStringField(fieldNames.getCallerClass(), callerData.getClassName(), isActive(fieldNames.getCallerClass()));
185 | ow.writeStringField(fieldNames.getCallerMethod(), callerData.getMethodName(), isActive(fieldNames.getCallerMethod()));
186 | ow.writeStringField(fieldNames.getCallerFile(), callerData.getFileName(), isActive(fieldNames.getCallerFile()));
187 | ow.writeNumberField(fieldNames.getCallerLine(), callerData.getLineNumber(), isActive(fieldNames.getCallerLine()));
188 | ow.done();
189 | }
190 | }
191 |
192 | protected void writeMarker(JsonWriter.ObjectWriter writer, ILoggingEvent event) throws IOException {
193 | Marker marker = event.getMarker();
194 | if (marker != null) {
195 | JsonWriter.ArrayWriter>> aw =
196 | writer.writeObject(fieldNames.getMarker(), isActive(fieldNames.getMarker())).writeArray(marker.getName(), true);
197 | Iterator markers = marker.iterator();
198 | while (markers.hasNext()) {
199 | String name = markers.next().getName();
200 | aw.writeString(name, name != null && !name.isEmpty());
201 | }
202 | aw.done().done();
203 | }
204 | }
205 |
206 | protected void writeMdc(JsonWriter.ObjectWriter writer, ILoggingEvent event) throws IOException {
207 | Map mdc = event.getMDCPropertyMap();
208 | if (mdc != null && !mdc.isEmpty()) {
209 | JsonWriter.ObjectWriter> ow =
210 | writer.writeObject(fieldNames.getMdc(), isActive(fieldNames.getMdc()));
211 | for (Map.Entry property : mdc.entrySet()) {
212 | ow.writeStringField(property.getKey(), property.getValue(), true);
213 | }
214 | ow.done();
215 | }
216 | }
217 |
218 | private boolean isActive(String name) {
219 | return !FieldNames.IGNORE_NAME.equals(name);
220 | }
221 |
222 | }
223 |
--------------------------------------------------------------------------------
/logback-ext-jackson/src/main/java/org/eluder/logback/ext/jackson/JsonWriter.java:
--------------------------------------------------------------------------------
1 | package org.eluder.logback.ext.jackson;
2 |
3 | /*
4 | * #[license]
5 | * logback-ext-jackson
6 | * %%
7 | * Copyright (C) 2014 - 2015 Tapio Rautonen
8 | * %%
9 | * Permission is hereby granted, free of charge, to any person obtaining a copy
10 | * of this software and associated documentation files (the "Software"), to deal
11 | * in the Software without restriction, including without limitation the rights
12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | * copies of the Software, and to permit persons to whom the Software is
14 | * furnished to do so, subject to the following conditions:
15 | *
16 | * The above copyright notice and this permission notice shall be included in
17 | * all copies or substantial portions of the Software.
18 | *
19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 | * THE SOFTWARE.
26 | * %[license]
27 | */
28 |
29 | import com.fasterxml.jackson.core.JsonGenerator;
30 | import com.fasterxml.jackson.core.util.MinimalPrettyPrinter;
31 | import com.fasterxml.jackson.databind.ObjectMapper;
32 |
33 | import java.io.Closeable;
34 | import java.io.IOException;
35 | import java.io.OutputStream;
36 | import java.io.OutputStreamWriter;
37 | import java.nio.charset.Charset;
38 |
39 | public class JsonWriter implements Closeable {
40 |
41 | private final JsonGenerator generator;
42 |
43 | public JsonWriter(OutputStream os, Charset charset, ObjectMapper mapper) throws IOException {
44 | generator = mapper.getFactory()
45 | .createGenerator(new OutputStreamWriter(os, charset))
46 | .configure(JsonGenerator.Feature.AUTO_CLOSE_TARGET, false)
47 | .setPrettyPrinter(new MinimalPrettyPrinter(""));
48 | }
49 |
50 | @Override
51 | public void close() throws IOException {
52 | generator.close();
53 | }
54 |
55 | public JsonWriter flush() throws IOException {
56 | generator.flush();
57 | return this;
58 | }
59 |
60 | public JsonWriter newline() throws IOException {
61 | generator.writeRaw('\n');
62 | return this;
63 | }
64 |
65 | public ObjectWriter writeObject() throws IOException {
66 | return new ObjectWriter(this, true);
67 | }
68 |
69 | public ArrayWriter writeArray() throws IOException {
70 | return new ArrayWriter(this, true);
71 | }
72 |
73 | public class ObjectWriter {
74 | private final T parent;
75 | private final boolean enabled;
76 |
77 | ObjectWriter(T parent, boolean parentEnabled) throws IOException {
78 | this.parent = parent;
79 | this.enabled = parentEnabled;
80 | if (enabled) {
81 | generator.writeStartObject();
82 | }
83 | }
84 |
85 | ObjectWriter(T parent, boolean parentEnabled, String name, boolean active) throws IOException {
86 | this.parent = parent;
87 | this.enabled = parentEnabled && active;
88 | if (enabled) {
89 | generator.writeObjectFieldStart(name);
90 | }
91 | }
92 |
93 | public ObjectWriter writeStringField(String name, String value, boolean active) throws IOException {
94 | if (enabled && active) {
95 | generator.writeStringField(name, value);
96 | }
97 | return this;
98 | }
99 |
100 | public ObjectWriter writeNumberField(String name, int value, boolean active) throws IOException {
101 | if (enabled && active) {
102 | generator.writeNumberField(name, value);
103 | }
104 | return this;
105 | }
106 |
107 | public ObjectWriter writeNumberField(String name, long value, boolean active) throws IOException {
108 | if (enabled && active) {
109 | generator.writeNumberField(name, value);
110 | }
111 | return this;
112 | }
113 |
114 | public ArrayWriter> writeArray(String name, boolean active) throws IOException {
115 | return new ArrayWriter>(this, enabled, name, active);
116 | }
117 |
118 | public ObjectWriter> writeObject(String name, boolean active) throws IOException {
119 | return new ObjectWriter>(this, enabled, name, active);
120 | }
121 |
122 | public T done() throws IOException {
123 | if (enabled) {
124 | generator.writeEndObject();
125 | }
126 | return parent;
127 | }
128 | }
129 |
130 | public class ArrayWriter {
131 | private final T parent;
132 | private final boolean enabled;
133 |
134 | ArrayWriter(T parent, boolean parentEnabled) throws IOException {
135 | this.parent = parent;
136 | this.enabled = parentEnabled;
137 | if (enabled) {
138 | generator.writeStartArray();
139 | }
140 | }
141 |
142 | ArrayWriter(T parent, boolean parentEnabled, String name, boolean active) throws IOException {
143 | this.parent = parent;
144 | this.enabled = parentEnabled && active;
145 | if (enabled) {
146 | generator.writeArrayFieldStart(name);
147 | }
148 | }
149 |
150 | public ArrayWriter writeString(String value, boolean active) throws IOException {
151 | if (enabled && active) {
152 | generator.writeString(value);
153 | }
154 | return this;
155 | }
156 |
157 | public ArrayWriter> writeArray() throws IOException {
158 | return new ArrayWriter>(this, enabled);
159 | }
160 |
161 | public ObjectWriter> writeObject() throws IOException {
162 | return new ObjectWriter>(this, enabled);
163 | }
164 |
165 | public T done() throws IOException {
166 | if (enabled) {
167 | generator.writeEndArray();
168 | }
169 | return parent;
170 | }
171 | }
172 |
173 | }
174 |
--------------------------------------------------------------------------------
/logback-ext-jackson/src/test/java/org/eluder/logback/ext/jackson/JacksonEncoderTest.java:
--------------------------------------------------------------------------------
1 | package org.eluder.logback.ext.jackson;
2 |
3 | /*
4 | * #[license]
5 | * logback-ext-jackson
6 | * %%
7 | * Copyright (C) 2014 - 2015 Tapio Rautonen
8 | * %%
9 | * Permission is hereby granted, free of charge, to any person obtaining a copy
10 | * of this software and associated documentation files (the "Software"), to deal
11 | * in the Software without restriction, including without limitation the rights
12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | * copies of the Software, and to permit persons to whom the Software is
14 | * furnished to do so, subject to the following conditions:
15 | *
16 | * The above copyright notice and this permission notice shall be included in
17 | * all copies or substantial portions of the Software.
18 | *
19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 | * THE SOFTWARE.
26 | * %[license]
27 | */
28 |
29 | import ch.qos.logback.classic.Level;
30 | import ch.qos.logback.classic.Logger;
31 | import ch.qos.logback.classic.LoggerContext;
32 | import ch.qos.logback.classic.spi.ILoggingEvent;
33 | import ch.qos.logback.classic.spi.LoggingEvent;
34 | import org.eluder.logback.ext.core.FieldNames;
35 | import org.junit.After;
36 | import org.junit.Before;
37 | import org.junit.Test;
38 |
39 | import java.io.ByteArrayOutputStream;
40 |
41 | import static org.junit.Assert.assertNotNull;
42 |
43 | public class JacksonEncoderTest {
44 |
45 | private final LoggerContext context = new LoggerContext();
46 | private final Logger logger = context.getLogger(JacksonEncoderTest.class);
47 |
48 | private JacksonEncoder encoder;
49 |
50 | @Before
51 | public void start() {
52 | encoder = new JacksonEncoder();
53 | encoder.start();
54 | }
55 |
56 | @After
57 | public void stop() {
58 | encoder.stop();
59 | encoder = null;
60 | }
61 |
62 | @Test
63 | public void encodeLoggingEvent() throws Exception {
64 | FieldNames fn = new FieldNames();
65 | fn.setLevel(FieldNames.IGNORE_NAME);
66 | encoder.setFieldNames(fn);
67 |
68 | ByteArrayOutputStream stream = new ByteArrayOutputStream();
69 | encoder.init(stream);
70 | encoder.doEncode(createLoggingEvent("Hellö JSON!"));
71 | String json = new String(stream.toByteArray(), "UTF-8");
72 | assertNotNull(json);
73 | }
74 |
75 | private ILoggingEvent createLoggingEvent(String message) {
76 | return new LoggingEvent("", logger, Level.DEBUG, message, new IllegalArgumentException("fobar"), null);
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/logback-ext-jackson/src/test/java/org/eluder/logback/ext/jackson/JsonWriterTest.java:
--------------------------------------------------------------------------------
1 | package org.eluder.logback.ext.jackson;
2 |
3 | /*
4 | * #[license]
5 | * logback-ext-jackson
6 | * %%
7 | * Copyright (C) 2014 - 2015 Tapio Rautonen
8 | * %%
9 | * Permission is hereby granted, free of charge, to any person obtaining a copy
10 | * of this software and associated documentation files (the "Software"), to deal
11 | * in the Software without restriction, including without limitation the rights
12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | * copies of the Software, and to permit persons to whom the Software is
14 | * furnished to do so, subject to the following conditions:
15 | *
16 | * The above copyright notice and this permission notice shall be included in
17 | * all copies or substantial portions of the Software.
18 | *
19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 | * THE SOFTWARE.
26 | * %[license]
27 | */
28 |
29 | import com.fasterxml.jackson.databind.ObjectMapper;
30 | import org.junit.Test;
31 |
32 | import java.io.ByteArrayOutputStream;
33 | import java.io.IOException;
34 | import java.io.OutputStream;
35 | import java.nio.charset.Charset;
36 |
37 | import static org.assertj.core.api.Assertions.*;
38 |
39 |
40 | public class JsonWriterTest {
41 |
42 | private final ObjectMapper mapper = new ObjectMapper();
43 | private final Charset charset = Charset.forName("UTF-8");
44 |
45 | @Test
46 | public void writeObject() throws Exception {
47 | ByteArrayOutputStream os = new ByteArrayOutputStream();
48 | JsonWriter writer = writer(os);
49 | writer.writeObject()
50 | .writeStringField("hello", "world", true)
51 | .writeNumberField("missing", 1, false)
52 | .done()
53 | .close();
54 |
55 | assertThat(os.toString(charset.name())).isEqualTo("{\"hello\":\"world\"}");
56 | }
57 |
58 | @Test
59 | public void writeArray() throws Exception {
60 | ByteArrayOutputStream os = new ByteArrayOutputStream();
61 | JsonWriter writer = writer(os);
62 | writer.writeArray()
63 | .writeString("hello", true)
64 | .writeString("world", false)
65 | .writeString("again", true)
66 | .done()
67 | .close();
68 |
69 | assertThat(os.toString(charset.name())).isEqualTo("[\"hello\",\"again\"]");
70 | }
71 |
72 | @Test
73 | public void writeComplexObject() throws Exception {
74 | ByteArrayOutputStream os = new ByteArrayOutputStream();
75 | JsonWriter writer = writer(os);
76 | writer.writeObject()
77 | .writeArray("array", true)
78 | .writeString("val1", true)
79 | .writeString("val2", true)
80 | .done()
81 | .writeObject("object", true)
82 | .writeArray("subarray", true)
83 | .writeObject()
84 | .writeNumberField("num", 10, true)
85 | .writeStringField("str", "test", true)
86 | .done()
87 | .writeArray()
88 | .writeString("sub1", true)
89 | .writeString("sub2", true)
90 | .done()
91 | .done()
92 | .writeStringField("theend", "yes", true)
93 | .done()
94 | .done()
95 | .close();
96 |
97 | assertThat(os.toString(charset.name())).isEqualTo("{\"array\":[\"val1\",\"val2\"],\"object\":{\"subarray\":[{\"num\":10,\"str\":\"test\"},[\"sub1\",\"sub2\"]],\"theend\":\"yes\"}}");
98 | }
99 |
100 | private JsonWriter writer(OutputStream os) throws IOException {
101 | return new JsonWriter(os, charset, mapper);
102 | }
103 |
104 | }
--------------------------------------------------------------------------------
/logback-ext-kinesis-appender/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | org.eluder.logback
8 | logback-ext
9 | 1.0-SNAPSHOT
10 |
11 |
12 | logback-ext-kinesis-appender
13 |
14 |
15 |
16 | ch.qos.logback
17 | logback-core
18 |
19 |
20 | ch.qos.logback
21 | logback-classic
22 |
23 |
24 | org.eluder.logback
25 | logback-ext-core
26 |
27 |
28 | org.eluder.logback
29 | logback-ext-aws-core
30 |
31 |
32 | org.eluder.logback
33 | logback-ext-jackson
34 |
35 |
36 | com.amazonaws
37 | aws-java-sdk-core
38 | ${aws.version}
39 |
40 |
41 | com.amazonaws
42 | aws-java-sdk-kinesis
43 | ${aws.version}
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/logback-ext-kinesis-appender/src/main/java/org/eluder/logback/ext/kinesis/appender/AsyncKinesisAppender.java:
--------------------------------------------------------------------------------
1 | package org.eluder.logback.ext.kinesis.appender;
2 |
3 | import ch.qos.logback.classic.AsyncAppender;
4 | import ch.qos.logback.classic.spi.ILoggingEvent;
5 | import ch.qos.logback.core.Context;
6 | import ch.qos.logback.core.Layout;
7 | import ch.qos.logback.core.encoder.Encoder;
8 |
9 | import java.nio.charset.Charset;
10 |
11 | public class AsyncKinesisAppender extends AsyncAppender {
12 |
13 | private final KinesisAppender appender;
14 |
15 | public AsyncKinesisAppender() {
16 | this(new KinesisAppender());
17 | }
18 |
19 | protected AsyncKinesisAppender(KinesisAppender appender) {
20 | appender.setAsyncParent(true);
21 | addAppender(appender);
22 | this.appender = appender;
23 | }
24 |
25 | public void setRegion(String region) {
26 | appender.setRegion(region);
27 | }
28 |
29 | public void setStream(String stream) {
30 | appender.setStream(stream);
31 | }
32 |
33 | public void setAccessKey(String accessKey) {
34 | appender.setAccessKey(accessKey);
35 | }
36 |
37 | public void setSecretKey(String secretKey) {
38 | appender.setSecretKey(secretKey);
39 | }
40 |
41 | public void setMaxPayloadSize(int maxPayloadSize) {
42 | appender.setMaxPayloadSize(maxPayloadSize);
43 | }
44 |
45 | public void setThreadPoolSize(int threadPoolSize) {
46 | appender.setThreadPoolSize(threadPoolSize);
47 | }
48 |
49 | @Override
50 | public final void setMaxFlushTime(int maxFlushTime) {
51 | appender.setMaxFlushTime(maxFlushTime);
52 | // add an extra 100 millis to wait for the internal event queue handling
53 | super.setMaxFlushTime(maxFlushTime + 100);
54 | }
55 |
56 | public void setCharset(Charset charset) {
57 | appender.setCharset(charset);
58 | }
59 |
60 | public void setEncoder(Encoder encoder) {
61 | appender.setEncoder(encoder);
62 | }
63 |
64 | public void setLayout(Layout layout) {
65 | appender.setLayout(layout);
66 | }
67 |
68 | public void setBinary(boolean binary) {
69 | appender.setBinary(binary);
70 | }
71 |
72 | @Override
73 | public void setName(String name) {
74 | appender.setName(name);
75 | super.setName(name);
76 | }
77 |
78 | @Override
79 | public void setContext(Context context) {
80 | appender.setContext(context);
81 | super.setContext(context);
82 | }
83 |
84 | @Override
85 | public void start() {
86 | appender.start();
87 | super.start();
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/logback-ext-kinesis-appender/src/main/java/org/eluder/logback/ext/kinesis/appender/KinesisAppender.java:
--------------------------------------------------------------------------------
1 | package org.eluder.logback.ext.kinesis.appender;
2 |
3 | import ch.qos.logback.core.spi.DeferredProcessingAware;
4 | import com.amazonaws.regions.RegionUtils;
5 | import com.amazonaws.services.kinesis.AmazonKinesisAsyncClient;
6 | import com.amazonaws.services.kinesis.model.PutRecordRequest;
7 | import com.amazonaws.services.kinesis.model.PutRecordResult;
8 | import com.amazonaws.util.StringUtils;
9 | import org.eluder.logback.ext.aws.core.AbstractAwsEncodingStringAppender;
10 | import org.eluder.logback.ext.aws.core.LoggingEventHandler;
11 | import org.eluder.logback.ext.core.AppenderExecutors;
12 | import org.eluder.logback.ext.core.ByteArrayPayloadConverter;
13 |
14 | import java.nio.ByteBuffer;
15 | import java.util.UUID;
16 | import java.util.concurrent.CountDownLatch;
17 |
18 | import static java.lang.String.format;
19 |
20 | public class KinesisAppender extends AbstractAwsEncodingStringAppender {
21 |
22 | private String region;
23 | private String stream;
24 |
25 | private AmazonKinesisAsyncClient kinesis;
26 |
27 | public final void setRegion(String region) {
28 | this.region = region;
29 | }
30 |
31 | public final void setStream(String stream) {
32 | this.stream = stream;
33 | }
34 |
35 | @Override
36 | public void start() {
37 | if (RegionUtils.getRegion(region) == null) {
38 | addError(format("Region not set or invalid for appender '%s'", getName()));
39 | return;
40 | }
41 | if (StringUtils.isNullOrEmpty(stream)) {
42 | addError(format("Stream not set for appender '%s", getName()));
43 | return;
44 | }
45 | setConverter(new ByteArrayPayloadConverter());
46 | super.start();
47 | }
48 |
49 | @Override
50 | protected void doStart() {
51 | kinesis = new AmazonKinesisAsyncClient(
52 | getCredentials(),
53 | getClientConfiguration(),
54 | AppenderExecutors.newExecutor(this, getThreadPoolSize())
55 | );
56 | kinesis.setRegion(RegionUtils.getRegion(region));
57 | }
58 |
59 | @Override
60 | protected void doStop() {
61 | if (kinesis != null) {
62 | AppenderExecutors.shutdown(this, kinesis.getExecutorService(), getMaxFlushTime());
63 | kinesis.shutdown();
64 | kinesis = null;
65 | }
66 | }
67 |
68 | @Override
69 | protected void handle(E event, byte[] encoded) throws Exception {
70 | ByteBuffer buffer = ByteBuffer.wrap(encoded);
71 | PutRecordRequest request = new PutRecordRequest()
72 | .withPartitionKey(getPartitionKey(event))
73 | .withStreamName(stream)
74 | .withData(buffer);
75 | String errorMessage = format("Appender '%s' failed to send logging event '%s' to Kinesis stream '%s'", getName(), event, stream);
76 | CountDownLatch latch = new CountDownLatch(isAsyncParent() ? 0 : 1);
77 | kinesis.putRecordAsync(request, new LoggingEventHandler(this, latch, errorMessage));
78 | AppenderExecutors.awaitLatch(this, latch, getMaxFlushTime());
79 | }
80 |
81 | protected String getPartitionKey(E event) {
82 | return UUID.randomUUID().toString();
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/logback-ext-lmax-appender/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 |
8 | logback-ext
9 | org.eluder.logback
10 | 1.0-SNAPSHOT
11 |
12 |
13 | logback-ext-lmax-appender
14 |
15 |
16 |
17 | ch.qos.logback
18 | logback-core
19 |
20 |
21 | ch.qos.logback
22 | logback-classic
23 | true
24 |
25 |
26 | ch.qos.logback
27 | logback-access
28 | true
29 |
30 |
31 | org.eluder.logback
32 | logback-ext-core
33 |
34 |
35 | com.lmax
36 | disruptor
37 | ${lmax.version}
38 |
39 |
40 |
41 | junit
42 | junit
43 | test
44 |
45 |
46 | org.openjdk.jmh
47 | jmh-core
48 | test
49 |
50 |
51 | org.openjdk.jmh
52 | jmh-generator-annprocess
53 | test
54 |
55 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/logback-ext-lmax-appender/src/main/java/org/eluder/logback/ext/lmax/appender/AccessEventDisruptorAppender.java:
--------------------------------------------------------------------------------
1 | package org.eluder.logback.ext.lmax.appender;
2 |
3 | import ch.qos.logback.access.spi.IAccessEvent;
4 |
5 | public class AccessEventDisruptorAppender extends DelegatingDisruptorAppender {
6 |
7 | }
8 |
--------------------------------------------------------------------------------
/logback-ext-lmax-appender/src/main/java/org/eluder/logback/ext/lmax/appender/DelegatingDisruptorAppender.java:
--------------------------------------------------------------------------------
1 | package org.eluder.logback.ext.lmax.appender;
2 |
3 | import ch.qos.logback.core.Appender;
4 | import ch.qos.logback.core.Context;
5 | import ch.qos.logback.core.spi.AppenderAttachable;
6 | import ch.qos.logback.core.spi.AppenderAttachableImpl;
7 | import ch.qos.logback.core.spi.DeferredProcessingAware;
8 | import com.lmax.disruptor.WorkHandler;
9 |
10 | import java.util.Iterator;
11 |
12 | public class DelegatingDisruptorAppender extends DisruptorAppender implements AppenderAttachable {
13 |
14 | private final AppenderAttachableImpl appenders = new AppenderAttachableImpl<>();
15 |
16 | public DelegatingDisruptorAppender() {
17 | setWorkHandler(new AppendersWorkHandler());
18 | }
19 |
20 | @Override
21 | public void start() {
22 | lock.lock();
23 | try {
24 | if (isStarted()) {
25 | return;
26 | }
27 | startDelegateAppenders();
28 | super.start();
29 | } finally {
30 | lock.unlock();
31 | }
32 | }
33 |
34 | @Override
35 | public void stop() {
36 | lock.lock();
37 | try {
38 | if (!isStarted()) {
39 | return;
40 | }
41 | super.stop();
42 | stopDelegateAppenders();
43 | } finally {
44 | lock.unlock();
45 | }
46 | }
47 |
48 | protected void startDelegateAppenders() {
49 | Iterator> iter = appenders.iteratorForAppenders();
50 | while (iter.hasNext()) {
51 | Appender appender = iter.next();
52 | if (!appender.isStarted()) {
53 | appender.start();
54 | }
55 | }
56 | }
57 |
58 | protected void stopDelegateAppenders() {
59 | Iterator> iter = appenders.iteratorForAppenders();
60 | while (iter.hasNext()) {
61 | Appender appender = iter.next();
62 | if (appender.isStarted()) {
63 | appender.stop();
64 | }
65 | }
66 | }
67 |
68 | @Override
69 | public void addAppender(Appender newAppender) {
70 | if (getContext() != null && newAppender.getContext() == null) {
71 | newAppender.setContext(getContext());
72 | }
73 | appenders.addAppender(newAppender);
74 | }
75 |
76 | @Override
77 | public Iterator> iteratorForAppenders() {
78 | return appenders.iteratorForAppenders();
79 | }
80 |
81 | @Override
82 | public Appender getAppender(String name) {
83 | return appenders.getAppender(name);
84 | }
85 |
86 | @Override
87 | public boolean isAttached(Appender appender) {
88 | return appenders.isAttached(appender);
89 | }
90 |
91 | @Override
92 | public void detachAndStopAllAppenders() {
93 | appenders.detachAndStopAllAppenders();
94 | }
95 |
96 | @Override
97 | public boolean detachAppender(Appender appender) {
98 | return appenders.detachAppender(appender);
99 | }
100 |
101 | @Override
102 | public boolean detachAppender(String name) {
103 | return appenders.detachAppender(name);
104 | }
105 |
106 | @Override
107 | public void setContext(Context context) {
108 | Iterator> iter = appenders.iteratorForAppenders();
109 | while (iter.hasNext()) {
110 | Appender appender = iter.next();
111 | if (appender.getContext() == null) {
112 | appender.setContext(context);
113 | }
114 | }
115 | super.setContext(context);
116 | }
117 |
118 | private class AppendersWorkHandler implements WorkHandler> {
119 | @Override
120 | public void onEvent(LogEvent event) throws Exception {
121 | appenders.appendLoopOnAppenders(event.event);
122 | }
123 | }
124 |
125 | }
126 |
--------------------------------------------------------------------------------
/logback-ext-lmax-appender/src/main/java/org/eluder/logback/ext/lmax/appender/DisruptorAppender.java:
--------------------------------------------------------------------------------
1 | package org.eluder.logback.ext.lmax.appender;
2 |
3 | import ch.qos.logback.core.UnsynchronizedAppenderBase;
4 | import ch.qos.logback.core.spi.ContextAware;
5 | import ch.qos.logback.core.spi.DeferredProcessingAware;
6 | import com.lmax.disruptor.EventFactory;
7 | import com.lmax.disruptor.EventTranslatorOneArg;
8 | import com.lmax.disruptor.ExceptionHandler;
9 | import com.lmax.disruptor.LifecycleAware;
10 | import com.lmax.disruptor.RingBuffer;
11 | import com.lmax.disruptor.TimeoutException;
12 | import com.lmax.disruptor.WaitStrategy;
13 | import com.lmax.disruptor.WorkHandler;
14 | import com.lmax.disruptor.dsl.Disruptor;
15 | import com.lmax.disruptor.dsl.ProducerType;
16 | import org.eluder.logback.ext.core.AppenderExecutors;
17 |
18 | import java.util.concurrent.ExecutorService;
19 | import java.util.concurrent.TimeUnit;
20 | import java.util.concurrent.locks.ReentrantLock;
21 |
22 | import static java.lang.String.format;
23 |
24 | public class DisruptorAppender extends UnsynchronizedAppenderBase {
25 |
26 | protected final ReentrantLock lock = new ReentrantLock(true);
27 |
28 | private static final int SLEEP_ON_DRAIN = 50;
29 | private static final int DEFAULT_THREAD_POOL_SIZE = 1;
30 | private static final int DEFAULT_BUFFER_SIZE = 8192;
31 |
32 | private EventFactory> eventFactory = new LogEventFactory<>();
33 | private EventTranslatorOneArg, E> eventTranslator = new LogEventTranslator<>();
34 | private ExceptionHandler> exceptionHandler = new LogExceptionHandler<>(this);
35 | private WorkHandler> workHandler;
36 |
37 | private int threadPoolSize = DEFAULT_THREAD_POOL_SIZE;
38 | private int bufferSize = DEFAULT_BUFFER_SIZE;
39 | private int maxFlushTime = AppenderExecutors.DEFAULT_MAX_FLUSH_TIME;
40 | private ProducerType producerType = ProducerType.MULTI;
41 | private WaitStrategy waitStrategy = WaitStrategyFactory.DEFAULT_WAIT_STRATEGY;
42 |
43 | private Disruptor> disruptor;
44 | private ExecutorService executor;
45 |
46 | public final void setEventFactory(EventFactory> eventFactory) {
47 | this.eventFactory = eventFactory;
48 | }
49 |
50 | public final void setEventTranslator(EventTranslatorOneArg, E> eventTranslator) {
51 | this.eventTranslator = eventTranslator;
52 | }
53 |
54 | public final void setExceptionHandler(ExceptionHandler> exceptionHandler) {
55 | this.exceptionHandler = exceptionHandler;
56 | }
57 |
58 | public final void setWorkHandler(WorkHandler> workHandler) {
59 | this.workHandler = workHandler;
60 | }
61 |
62 | public final void setThreadPoolSize(int threadPoolSize) {
63 | this.threadPoolSize = threadPoolSize;
64 | }
65 |
66 | public final void setBufferSize(int bufferSize) {
67 | this.bufferSize = bufferSize;
68 | }
69 |
70 | public final void setMaxFlushTime(int maxFlushTime) {
71 | this.maxFlushTime = maxFlushTime;
72 | }
73 |
74 | public final void setProducerType(ProducerType producerType) {
75 | this.producerType = producerType;
76 | }
77 |
78 | public final void setWaitStrategy(WaitStrategy waitStrategy) {
79 | this.waitStrategy = waitStrategy;
80 | }
81 |
82 | public final void setWaitStrategyType(String waitStrategyType) {
83 | setWaitStrategy(WaitStrategyFactory.createFromType(waitStrategyType));
84 | }
85 |
86 | @Override
87 | @SuppressWarnings("unchecked")
88 | public void start() {
89 | if (workHandler == null) {
90 | addError(format("Event handler not set for appender '%s'", getName()));
91 | return;
92 | }
93 | lock.lock();
94 | try {
95 | if (isStarted()) {
96 | return;
97 | }
98 | executor = AppenderExecutors.newExecutor(this, threadPoolSize);
99 | disruptor = new Disruptor<>(
100 | eventFactory,
101 | bufferSize,
102 | executor,
103 | producerType,
104 | waitStrategy
105 | );
106 | disruptor.handleExceptionsWith(exceptionHandler);
107 | disruptor.handleEventsWithWorkerPool(createWorkers());
108 | disruptor.start();
109 | super.start();
110 | } finally {
111 | lock.unlock();
112 | }
113 | }
114 |
115 | @Override
116 | public void stop() {
117 | lock.lock();
118 | try {
119 | if (!isStarted()) {
120 | return;
121 | }
122 | super.stop();
123 | shutdownDisruptor();
124 | executor.shutdownNow();
125 | } finally {
126 | lock.unlock();
127 | }
128 | }
129 |
130 | @Override
131 | protected void append(E eventObject) {
132 | prepareForDeferredProcessing(eventObject);
133 | disruptor.publishEvent(eventTranslator, eventObject);
134 | }
135 |
136 | protected void prepareForDeferredProcessing(E event) {
137 | event.prepareForDeferredProcessing();
138 | }
139 |
140 | @SuppressWarnings("unchecked")
141 | private WorkHandler>[] createWorkers() {
142 | WorkHandler> handler = new ClearingWorkHandler<>(workHandler);
143 | WorkHandler>[] workers = new WorkHandler[threadPoolSize];
144 | for (int i = 0; i < threadPoolSize; i++) {
145 | workers[i] = handler;
146 | }
147 | return workers;
148 | }
149 |
150 | private void shutdownDisruptor() {
151 | // disruptor busy waits while shutting down so this is a workaround
152 | // for not to hog all the CPU on shutdown
153 | long until = System.currentTimeMillis() + maxFlushTime;
154 | while (System.currentTimeMillis() < until && hashBackLog()) {
155 | try {
156 | Thread.sleep(SLEEP_ON_DRAIN);
157 | } catch (InterruptedException ex) {
158 | // noop
159 | }
160 | }
161 | try {
162 | disruptor.shutdown(0, TimeUnit.MILLISECONDS);
163 | } catch (TimeoutException ex) {
164 | addWarn(format("Disruptor did not shut down in %d milliseconds, " +
165 | "logging events might have been discarded",
166 | maxFlushTime));
167 | }
168 | }
169 |
170 | private boolean hashBackLog() {
171 | RingBuffer> buffer = disruptor.getRingBuffer();
172 | return !buffer.hasAvailableCapacity(buffer.getBufferSize());
173 | }
174 |
175 | protected static class LogEvent {
176 | public volatile E event;
177 | }
178 |
179 | protected static class LogEventFactory implements EventFactory> {
180 | @Override
181 | public LogEvent newInstance() {
182 | return new LogEvent<>();
183 | }
184 | }
185 |
186 | protected static class LogEventTranslator implements EventTranslatorOneArg, E> {
187 | @Override
188 | public void translateTo(LogEvent event, long sequence, E arg0) {
189 | event.event = arg0;
190 | }
191 | }
192 |
193 | protected static class LogExceptionHandler implements ExceptionHandler> {
194 |
195 | private final ContextAware context;
196 |
197 | public LogExceptionHandler(ContextAware context) {
198 | this.context = context;
199 | }
200 |
201 | @Override
202 | public void handleEventException(Throwable ex, long sequence, LogEvent event) {
203 | if (ex instanceof InterruptedException) {
204 | context.addWarn("Disruptor was interrupted while processing event");
205 | } else {
206 | context.addError("Failed to process event", ex);
207 | }
208 | }
209 |
210 | @Override
211 | public void handleOnStartException(Throwable ex) {
212 | context.addError("Failed to start disruptor", ex);
213 | }
214 |
215 | @Override
216 | public void handleOnShutdownException(Throwable ex) {
217 | context.addError("Failed to shutdown disruptor", ex);
218 | }
219 | }
220 |
221 | /**
222 | * Clears logback event objects from distruptor event context to allow proper garbage collecting.
223 | */
224 | private static class ClearingWorkHandler implements WorkHandler>, LifecycleAware {
225 |
226 | private final WorkHandler> delegate;
227 | private boolean started = false;
228 |
229 | public ClearingWorkHandler(WorkHandler> delegate) {
230 | this.delegate = delegate;
231 | }
232 |
233 | @Override
234 | public void onEvent(LogEvent event) throws Exception {
235 | try {
236 | delegate.onEvent(event);
237 | } finally {
238 | event.event = null;
239 | }
240 | }
241 |
242 | @Override
243 | public synchronized void onStart() {
244 | if (!started) {
245 | if (delegate instanceof LifecycleAware) {
246 | ((LifecycleAware) delegate).onStart();
247 | }
248 | started = true;
249 | }
250 | }
251 |
252 | @Override
253 | public synchronized void onShutdown() {
254 | if (started) {
255 | started = false;
256 | if (delegate instanceof LifecycleAware) {
257 | ((LifecycleAware) delegate).onShutdown();
258 | }
259 | }
260 | }
261 | }
262 | }
263 |
--------------------------------------------------------------------------------
/logback-ext-lmax-appender/src/main/java/org/eluder/logback/ext/lmax/appender/LoggingEventDisruptorAppender.java:
--------------------------------------------------------------------------------
1 | package org.eluder.logback.ext.lmax.appender;
2 |
3 | import ch.qos.logback.classic.spi.ILoggingEvent;
4 |
5 | public class LoggingEventDisruptorAppender extends DelegatingDisruptorAppender {
6 |
7 | private boolean includeCallerData = false;
8 |
9 | public final boolean isIncludeCallerData() {
10 | return includeCallerData;
11 | }
12 |
13 | public final void setIncludeCallerData(boolean includeCallerData) {
14 | this.includeCallerData = includeCallerData;
15 | }
16 |
17 | @Override
18 | protected void prepareForDeferredProcessing(ILoggingEvent event) {
19 | super.prepareForDeferredProcessing(event);
20 | if (includeCallerData) {
21 | event.getCallerData();
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/logback-ext-lmax-appender/src/main/java/org/eluder/logback/ext/lmax/appender/WaitStrategyFactory.java:
--------------------------------------------------------------------------------
1 | package org.eluder.logback.ext.lmax.appender;
2 |
3 | import com.lmax.disruptor.BlockingWaitStrategy;
4 | import com.lmax.disruptor.BusySpinWaitStrategy;
5 | import com.lmax.disruptor.SleepingWaitStrategy;
6 | import com.lmax.disruptor.WaitStrategy;
7 | import com.lmax.disruptor.YieldingWaitStrategy;
8 |
9 | public class WaitStrategyFactory {
10 |
11 | public static final WaitStrategy DEFAULT_WAIT_STRATEGY = new BlockingWaitStrategy();
12 |
13 | public static WaitStrategy createFromType(String name) {
14 | if ("BusySpin".equalsIgnoreCase(name)) {
15 | return new BusySpinWaitStrategy();
16 | } else if ("Blocking".equalsIgnoreCase(name)) {
17 | return new BlockingWaitStrategy();
18 | } else if ("Yielding".equalsIgnoreCase(name)) {
19 | return new YieldingWaitStrategy();
20 | } else if ("Sleeping".equalsIgnoreCase(name)) {
21 | return new SleepingWaitStrategy();
22 | } else {
23 | throw new IllegalArgumentException("Invalid or unsupported wait strategy type '" + name + "'");
24 | }
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/logback-ext-lmax-appender/src/test/java/org/eluder/logback/ext/lmax/appender/AppenderBenchmark.java:
--------------------------------------------------------------------------------
1 | package org.eluder.logback.ext.lmax.appender;
2 |
3 | import ch.qos.logback.classic.Logger;
4 | import ch.qos.logback.classic.LoggerContext;
5 | import ch.qos.logback.classic.spi.ILoggingEvent;
6 | import ch.qos.logback.classic.spi.LoggingEvent;
7 | import ch.qos.logback.core.Appender;
8 | import ch.qos.logback.core.UnsynchronizedAppenderBase;
9 | import ch.qos.logback.core.spi.AppenderAttachable;
10 | import org.openjdk.jmh.annotations.*;
11 | import org.slf4j.LoggerFactory;
12 |
13 | import java.util.concurrent.CountDownLatch;
14 | import java.util.concurrent.TimeUnit;
15 | import java.util.concurrent.atomic.AtomicInteger;
16 |
17 | @State(Scope.Benchmark)
18 | public abstract class AppenderBenchmark & AppenderAttachable> {
19 |
20 | private static final int EVENTS = 32000;
21 | private static final LoggerContext CONTEXT = (LoggerContext) LoggerFactory.getILoggerFactory();
22 |
23 | private static final AtomicInteger idx = new AtomicInteger(0);
24 | private static final CountDownLatch[] latches = new CountDownLatch[99999];
25 |
26 |
27 | @Param({ "1" })
28 | public int consumers;
29 |
30 | private EmulatingAppender controller;
31 | private T appender;
32 |
33 | @State(Scope.Thread)
34 | public static class ThreadContext {
35 | LoggingEvent event;
36 | int index;
37 |
38 | @Setup(Level.Trial)
39 | public void setupContext() {
40 | index = idx.getAndIncrement();
41 | event = new LoggingEvent(
42 | "org.eluder.logback.ext.lmax.appender.AppenderBenchmark",
43 | CONTEXT.getLogger(Logger.ROOT_LOGGER_NAME), ch.qos.logback.classic.Level.INFO, "" + index, null, null
44 | );
45 | }
46 |
47 | @Setup(Level.Invocation)
48 | public void setupLatch() {
49 | latches[index] = new CountDownLatch(EVENTS);
50 | }
51 |
52 | public void await() throws InterruptedException {
53 | latches[index].await();
54 | }
55 | }
56 |
57 | @Setup(Level.Trial)
58 | public void setupAppender() {
59 | controller = new EmulatingAppender(CONTEXT);
60 | appender = createAppender(consumers);
61 | appender.setContext(CONTEXT);
62 | appender.addAppender(controller);
63 |
64 | controller.start();
65 | appender.start();
66 | }
67 |
68 | @TearDown(Level.Trial)
69 | public void tearDownAppender() {
70 | appender.stop();
71 | controller.stop();
72 | }
73 |
74 | protected abstract T createAppender(int consumers);
75 |
76 | @Benchmark
77 | @BenchmarkMode(Mode.Throughput)
78 | @OutputTimeUnit(TimeUnit.SECONDS)
79 | @OperationsPerInvocation(EVENTS)
80 | public void throughput(ThreadContext context) throws Exception {
81 | append(context);
82 | }
83 |
84 | @Benchmark
85 | @BenchmarkMode(Mode.SampleTime)
86 | @OutputTimeUnit(TimeUnit.NANOSECONDS)
87 | @OperationsPerInvocation(EVENTS)
88 | public void latency(ThreadContext context) throws Exception {
89 | append(context);
90 | }
91 |
92 | private void append(ThreadContext context) throws Exception {
93 | for (int i = 0; i < EVENTS; i++) {
94 | appender.doAppend(context.event);
95 | }
96 | context.await();
97 | }
98 |
99 | public static class EmulatingAppender extends UnsynchronizedAppenderBase {
100 |
101 | public EmulatingAppender(LoggerContext context) {
102 | setContext(context);
103 | setName("emulator");
104 | }
105 |
106 | @Override
107 | protected void append(ILoggingEvent eventObject) {
108 | int index = Integer.parseInt(eventObject.getMessage());
109 | latches[index].countDown();
110 | }
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/logback-ext-lmax-appender/src/test/java/org/eluder/logback/ext/lmax/appender/AsyncAppenderBenchmark.java:
--------------------------------------------------------------------------------
1 | package org.eluder.logback.ext.lmax.appender;
2 |
3 | import ch.qos.logback.classic.AsyncAppender;
4 |
5 | public class AsyncAppenderBenchmark extends AppenderBenchmark {
6 |
7 | @Override
8 | protected AsyncAppender createAppender(int consumers) {
9 | AsyncAppender appender = new AsyncAppender();
10 | appender.setName("async");
11 | appender.setQueueSize(1048576);
12 | return appender;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/logback-ext-lmax-appender/src/test/java/org/eluder/logback/ext/lmax/appender/DisruptorAppenderBenchmark.java:
--------------------------------------------------------------------------------
1 | package org.eluder.logback.ext.lmax.appender;
2 |
3 | public class DisruptorAppenderBenchmark extends AppenderBenchmark {
4 |
5 | @Override
6 | protected LoggingEventDisruptorAppender createAppender(int consumers) {
7 | LoggingEventDisruptorAppender appender = new LoggingEventDisruptorAppender();
8 | appender.setName("disruptor");
9 | appender.setBufferSize(1048576);
10 | appender.setThreadPoolSize(consumers);
11 | return appender;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/logback-ext-lmax-appender/src/test/java/org/eluder/logback/ext/lmax/appender/LoggingEventDisruptorAppenderPerf.java:
--------------------------------------------------------------------------------
1 | package org.eluder.logback.ext.lmax.appender;
2 |
3 | import org.junit.Test;
4 | import org.openjdk.jmh.runner.Runner;
5 | import org.openjdk.jmh.runner.options.Options;
6 | import org.openjdk.jmh.runner.options.OptionsBuilder;
7 | import org.openjdk.jmh.runner.options.TimeValue;
8 |
9 | public class LoggingEventDisruptorAppenderPerf {
10 |
11 | @Test
12 | public void benchmarkTest() throws Exception {
13 | Options options = new OptionsBuilder()
14 | .include(AsyncAppenderBenchmark.class.getSimpleName())
15 | .include(DisruptorAppenderBenchmark.class.getSimpleName())
16 | .warmupTime(TimeValue.seconds(5))
17 | .warmupIterations(2)
18 | .measurementTime(TimeValue.seconds(10))
19 | .measurementIterations(10)
20 | .threads(16)
21 | .forks(1)
22 | .build();
23 | new Runner(options).run();
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/logback-ext-sns-appender/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | logback-ext
8 | org.eluder.logback
9 | 1.0-SNAPSHOT
10 |
11 |
12 | logback-ext-sns-appender
13 |
14 |
15 |
16 | ch.qos.logback
17 | logback-core
18 |
19 |
20 | ch.qos.logback
21 | logback-classic
22 |
23 |
24 | org.eluder.logback
25 | logback-ext-core
26 |
27 |
28 | org.eluder.logback
29 | logback-ext-aws-core
30 |
31 |
32 | com.amazonaws
33 | aws-java-sdk-core
34 | ${aws.version}
35 |
36 |
37 | com.amazonaws
38 | aws-java-sdk-sns
39 | ${aws.version}
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/logback-ext-sns-appender/src/main/java/org/eluder/logback/ext/sns/appender/AsyncSnsAppender.java:
--------------------------------------------------------------------------------
1 | package org.eluder.logback.ext.sns.appender;
2 |
3 | /*
4 | * #[license]
5 | * logback-ext-sns-appender
6 | * %%
7 | * Copyright (C) 2014 - 2015 Tapio Rautonen
8 | * %%
9 | * Permission is hereby granted, free of charge, to any person obtaining a copy
10 | * of this software and associated documentation files (the "Software"), to deal
11 | * in the Software without restriction, including without limitation the rights
12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | * copies of the Software, and to permit persons to whom the Software is
14 | * furnished to do so, subject to the following conditions:
15 | *
16 | * The above copyright notice and this permission notice shall be included in
17 | * all copies or substantial portions of the Software.
18 | *
19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 | * THE SOFTWARE.
26 | * %[license]
27 | */
28 |
29 | import ch.qos.logback.classic.AsyncAppender;
30 | import ch.qos.logback.classic.spi.ILoggingEvent;
31 | import ch.qos.logback.core.Context;
32 | import ch.qos.logback.core.Layout;
33 | import ch.qos.logback.core.encoder.Encoder;
34 |
35 | import java.nio.charset.Charset;
36 |
37 | public class AsyncSnsAppender extends AsyncAppender {
38 |
39 | private final SnsAppender appender;
40 |
41 | public AsyncSnsAppender() {
42 | this(new SnsAppender());
43 | }
44 |
45 | protected AsyncSnsAppender(SnsAppender appender) {
46 | appender.setAsyncParent(true);
47 | addAppender(appender);
48 | this.appender = appender;
49 | }
50 |
51 | public void setAccessKey(String accessKey) {
52 | appender.setAccessKey(accessKey);
53 | }
54 |
55 | public void setSecretKey(String secretKey) {
56 | appender.setSecretKey(secretKey);
57 | }
58 |
59 | public void setMaxPayloadSize(int maxPayloadSize) {
60 | appender.setMaxPayloadSize(maxPayloadSize);
61 | }
62 |
63 | public void setRegion(String region) {
64 | appender.setRegion(region);
65 | }
66 |
67 | public void setTopic(String topic) {
68 | appender.setTopic(topic);
69 | }
70 |
71 | public void setSubject(String subject) {
72 | appender.setSubject(subject);
73 | }
74 |
75 | public final void setThreadPoolSize(int threadPoolSize) {
76 | appender.setThreadPoolSize(threadPoolSize);
77 | }
78 |
79 | @Override
80 | public final void setMaxFlushTime(int maxFlushTime) {
81 | appender.setMaxFlushTime(maxFlushTime);
82 | // add an extra 100 millis to wait for the internal event queue handling
83 | super.setMaxFlushTime(maxFlushTime + 100);
84 | }
85 |
86 | public void setCharset(Charset charset) {
87 | appender.setCharset(charset);
88 | }
89 |
90 | public void setEncoder(Encoder encoder) {
91 | appender.setEncoder(encoder);
92 | }
93 |
94 | public void setLayout(Layout layout) {
95 | appender.setLayout(layout);
96 | }
97 |
98 | public void setBinary(boolean binary) {
99 | appender.setBinary(binary);
100 | }
101 |
102 | @Override
103 | public void setName(String name) {
104 | appender.setName(name);
105 | super.setName(name);
106 | }
107 |
108 | @Override
109 | public void setContext(Context context) {
110 | appender.setContext(context);
111 | super.setContext(context);
112 | }
113 |
114 | @Override
115 | public void start() {
116 | appender.start();
117 | super.start();
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/logback-ext-sns-appender/src/main/java/org/eluder/logback/ext/sns/appender/SnsAppender.java:
--------------------------------------------------------------------------------
1 | package org.eluder.logback.ext.sns.appender;
2 |
3 | /*
4 | * #[license]
5 | * logback-ext-sns-appender
6 | * %%
7 | * Copyright (C) 2014 - 2015 Tapio Rautonen
8 | * %%
9 | * Permission is hereby granted, free of charge, to any person obtaining a copy
10 | * of this software and associated documentation files (the "Software"), to deal
11 | * in the Software without restriction, including without limitation the rights
12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | * copies of the Software, and to permit persons to whom the Software is
14 | * furnished to do so, subject to the following conditions:
15 | *
16 | * The above copyright notice and this permission notice shall be included in
17 | * all copies or substantial portions of the Software.
18 | *
19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 | * THE SOFTWARE.
26 | * %[license]
27 | */
28 |
29 | import ch.qos.logback.core.filter.Filter;
30 | import ch.qos.logback.core.spi.DeferredProcessingAware;
31 | import com.amazonaws.regions.RegionUtils;
32 | import com.amazonaws.services.sns.AmazonSNSAsyncClient;
33 | import com.amazonaws.services.sns.model.PublishRequest;
34 | import com.amazonaws.services.sns.model.PublishResult;
35 | import org.eluder.logback.ext.aws.core.AbstractAwsEncodingStringAppender;
36 | import org.eluder.logback.ext.aws.core.AwsSupport;
37 | import org.eluder.logback.ext.aws.core.LoggingEventHandler;
38 | import org.eluder.logback.ext.core.AppenderExecutors;
39 | import org.eluder.logback.ext.core.StringPayloadConverter;
40 |
41 | import java.util.concurrent.CountDownLatch;
42 | import java.util.concurrent.Executors;
43 |
44 | import static java.lang.String.format;
45 |
46 | public class SnsAppender extends AbstractAwsEncodingStringAppender {
47 |
48 | private String region;
49 | private String topic;
50 | private String subject;
51 |
52 | private AmazonSNSAsyncClient sns;
53 |
54 | public SnsAppender() {
55 | super();
56 | }
57 |
58 | protected SnsAppender(AwsSupport awsSupport, Filter sdkLoggingFilter) {
59 | super(awsSupport, sdkLoggingFilter);
60 | }
61 |
62 | public void setRegion(String region) {
63 | this.region = region;
64 | }
65 |
66 | public void setTopic(String topic) {
67 | this.topic = topic;
68 | }
69 |
70 | public void setSubject(String subject) {
71 | this.subject = subject;
72 | }
73 |
74 | @Override
75 | public void start() {
76 | setConverter(new StringPayloadConverter(getCharset(), isBinary()));
77 | super.start();
78 | }
79 |
80 | @Override
81 | protected void doStart() {
82 | sns = new AmazonSNSAsyncClient(
83 | getCredentials(),
84 | getClientConfiguration(),
85 | Executors.newFixedThreadPool(getThreadPoolSize())
86 | );
87 | sns.setRegion(RegionUtils.getRegion(region));
88 | }
89 |
90 | @Override
91 | protected void doStop() {
92 | if (sns != null) {
93 | AppenderExecutors.shutdown(this, sns.getExecutorService(), getMaxFlushTime());
94 | sns.shutdown();
95 | sns = null;
96 | }
97 | }
98 |
99 | @Override
100 | protected void handle(final E event, final String encoded) throws Exception {
101 | PublishRequest request = new PublishRequest(topic, encoded, subject);
102 | String errorMessage = format("Appender '%s' failed to send logging event '%s' to SNS topic '%s'", getName(), event, topic);
103 | CountDownLatch latch = new CountDownLatch(isAsyncParent() ? 0 : 1);
104 | sns.publishAsync(request, new LoggingEventHandler(this, latch, errorMessage));
105 | AppenderExecutors.awaitLatch(this, latch, getMaxFlushTime());
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/logback-ext-sqs-appender/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | logback-ext
8 | org.eluder.logback
9 | 1.0-SNAPSHOT
10 |
11 |
12 | logback-ext-sqs-appender
13 |
14 |
15 |
16 | ch.qos.logback
17 | logback-core
18 |
19 |
20 | ch.qos.logback
21 | logback-classic
22 |
23 |
24 | org.eluder.logback
25 | logback-ext-core
26 |
27 |
28 | org.eluder.logback
29 | logback-ext-aws-core
30 |
31 |
32 | com.amazonaws
33 | aws-java-sdk-core
34 | ${aws.version}
35 |
36 |
37 | com.amazonaws
38 | aws-java-sdk-sqs
39 | ${aws.version}
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/logback-ext-sqs-appender/src/main/java/org/eluder/logback/ext/sqs/appender/AsyncSqsAppender.java:
--------------------------------------------------------------------------------
1 | package org.eluder.logback.ext.sqs.appender;
2 |
3 | /*
4 | * #[license]
5 | * logback-ext-sqs-appender
6 | * %%
7 | * Copyright (C) 2014 - 2015 Tapio Rautonen
8 | * %%
9 | * Permission is hereby granted, free of charge, to any person obtaining a copy
10 | * of this software and associated documentation files (the "Software"), to deal
11 | * in the Software without restriction, including without limitation the rights
12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | * copies of the Software, and to permit persons to whom the Software is
14 | * furnished to do so, subject to the following conditions:
15 | *
16 | * The above copyright notice and this permission notice shall be included in
17 | * all copies or substantial portions of the Software.
18 | *
19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 | * THE SOFTWARE.
26 | * %[license]
27 | */
28 |
29 | import ch.qos.logback.classic.AsyncAppender;
30 | import ch.qos.logback.classic.spi.ILoggingEvent;
31 | import ch.qos.logback.core.Context;
32 | import ch.qos.logback.core.Layout;
33 | import ch.qos.logback.core.encoder.Encoder;
34 |
35 | import java.nio.charset.Charset;
36 |
37 | public class AsyncSqsAppender extends AsyncAppender {
38 |
39 | private final SqsAppender appender;
40 |
41 | public AsyncSqsAppender() {
42 | this(new SqsAppender());
43 | }
44 |
45 | protected AsyncSqsAppender(SqsAppender appender) {
46 | appender.setAsyncParent(true);
47 | addAppender(appender);
48 | this.appender = appender;
49 | }
50 |
51 | public void setQueueUrl(String queueUrl) {
52 | appender.setQueueUrl(queueUrl);
53 | }
54 |
55 | public void setAccessKey(String accessKey) {
56 | appender.setAccessKey(accessKey);
57 | }
58 |
59 | public void setSecretKey(String secretKey) {
60 | appender.setSecretKey(secretKey);
61 | }
62 |
63 | public void setMaxPayloadSize(int maxPayloadSize) {
64 | appender.setMaxPayloadSize(maxPayloadSize);
65 | }
66 |
67 | public final void setThreadPoolSize(int threadPoolSize) {
68 | appender.setThreadPoolSize(threadPoolSize);
69 | }
70 |
71 | @Override
72 | public final void setMaxFlushTime(int maxFlushTime) {
73 | appender.setMaxFlushTime(maxFlushTime);
74 | // add an extra 100 millis to wait for the internal event queue handling
75 | super.setMaxFlushTime(maxFlushTime + 100);
76 | }
77 |
78 | public void setCharset(Charset charset) {
79 | appender.setCharset(charset);
80 | }
81 |
82 | public void setEncoder(Encoder encoder) {
83 | appender.setEncoder(encoder);
84 | }
85 |
86 | public void setLayout(Layout layout) {
87 | appender.setLayout(layout);
88 | }
89 |
90 | public void setBinary(boolean binary) {
91 | appender.setBinary(binary);
92 | }
93 |
94 | @Override
95 | public void setName(String name) {
96 | appender.setName(name);
97 | super.setName(name);
98 | }
99 |
100 | @Override
101 | public void setContext(Context context) {
102 | appender.setContext(context);
103 | super.setContext(context);
104 | }
105 |
106 | @Override
107 | public void start() {
108 | appender.start();
109 | super.start();
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/logback-ext-sqs-appender/src/main/java/org/eluder/logback/ext/sqs/appender/SqsAppender.java:
--------------------------------------------------------------------------------
1 | package org.eluder.logback.ext.sqs.appender;
2 |
3 | /*
4 | * #[license]
5 | * logback-ext-sqs-appender
6 | * %%
7 | * Copyright (C) 2014 - 2015 Tapio Rautonen
8 | * %%
9 | * Permission is hereby granted, free of charge, to any person obtaining a copy
10 | * of this software and associated documentation files (the "Software"), to deal
11 | * in the Software without restriction, including without limitation the rights
12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | * copies of the Software, and to permit persons to whom the Software is
14 | * furnished to do so, subject to the following conditions:
15 | *
16 | * The above copyright notice and this permission notice shall be included in
17 | * all copies or substantial portions of the Software.
18 | *
19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 | * THE SOFTWARE.
26 | * %[license]
27 | */
28 |
29 | import ch.qos.logback.core.filter.Filter;
30 | import ch.qos.logback.core.spi.DeferredProcessingAware;
31 | import com.amazonaws.services.sqs.AmazonSQSAsyncClient;
32 | import com.amazonaws.services.sqs.model.SendMessageRequest;
33 | import com.amazonaws.services.sqs.model.SendMessageResult;
34 | import org.eluder.logback.ext.aws.core.AbstractAwsEncodingStringAppender;
35 | import org.eluder.logback.ext.aws.core.AwsSupport;
36 | import org.eluder.logback.ext.aws.core.LoggingEventHandler;
37 | import org.eluder.logback.ext.core.AppenderExecutors;
38 | import org.eluder.logback.ext.core.StringPayloadConverter;
39 |
40 | import java.net.URI;
41 | import java.net.URISyntaxException;
42 | import java.util.concurrent.CountDownLatch;
43 | import java.util.concurrent.Executors;
44 |
45 | import static java.lang.String.format;
46 |
47 | public class SqsAppender extends AbstractAwsEncodingStringAppender {
48 |
49 | private String queueUrl;
50 |
51 | private AmazonSQSAsyncClient sqs;
52 |
53 | public SqsAppender() {
54 | super();
55 | }
56 |
57 | protected SqsAppender(AwsSupport awsSupport, Filter sdkLoggingFilter) {
58 | super(awsSupport, sdkLoggingFilter);
59 | }
60 |
61 | public final void setQueueUrl(String queueUrl) {
62 | this.queueUrl = queueUrl;
63 | }
64 |
65 | @Override
66 | public void start() {
67 | if (queueUrl == null) {
68 | addError(format("Queue url not set for appender '%s'", getName()));
69 | return;
70 | }
71 | setConverter(new StringPayloadConverter(getCharset(), isBinary()));
72 | super.start();
73 | }
74 |
75 | @Override
76 | protected void doStart() {
77 | sqs = new AmazonSQSAsyncClient(
78 | getCredentials(),
79 | getClientConfiguration(),
80 | Executors.newFixedThreadPool(getThreadPoolSize())
81 | );
82 | sqs.setEndpoint(getEndpoint());
83 | }
84 |
85 | @Override
86 | protected void doStop() {
87 | if (sqs != null) {
88 | AppenderExecutors.shutdown(this, sqs.getExecutorService(), getMaxFlushTime());
89 | sqs.shutdown();
90 | sqs = null;
91 | }
92 | }
93 |
94 | protected String getEndpoint() {
95 | try {
96 | return new URI(queueUrl).getHost();
97 | } catch (URISyntaxException ex) {
98 | throw new IllegalArgumentException("Malformed queue url", ex);
99 | }
100 | }
101 |
102 | @Override
103 | protected void handle(final E event, final String encoded) throws Exception {
104 | SendMessageRequest request = new SendMessageRequest(queueUrl, encoded);
105 | String errorMessage = format("Appender '%s' failed to send logging event '%s' to SQS queue '%s'", getName(), event, queueUrl);
106 | CountDownLatch latch = new CountDownLatch(isAsyncParent() ? 0 : 1);
107 | sqs.sendMessageAsync(request, new LoggingEventHandler(this, latch, errorMessage));
108 | AppenderExecutors.awaitLatch(this, latch, getMaxFlushTime());
109 | }
110 |
111 | }
112 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 |
8 | eluder-parent
9 | org.eluder
10 | 8-SNAPSHOT
11 |
12 |
13 |
14 | org.eluder.logback
15 | logback-ext
16 | 1.0-SNAPSHOT
17 | pom
18 |
19 | logback-ext
20 | Extensions for Logback logging library.
21 | https://github.com/trautonen/logback-ext
22 | 2014
23 |
24 |
25 |
26 | Tapio Rautonen
27 |
28 |
29 |
30 |
31 |
32 | The MIT License (MIT)
33 | http://opensource.org/licenses/MIT
34 | repo
35 |
36 |
37 |
38 |
39 | scm:git:git://github.com/trautonen/logback-ext.git
40 | scm:git:git://github.com/trautonen/logback-ext.git
41 | https://github.com/trautonen/logback-ext
42 |
43 |
44 |
45 | 1.7
46 | 18.0
47 | 2.5.4
48 | 1.10.2
49 | 3.3.2
50 | 1.10.5
51 |
52 |
53 |
54 | logback-ext-core
55 | logback-ext-jackson
56 | logback-ext-aws-core
57 | logback-ext-sqs-appender
58 | logback-ext-sns-appender
59 | logback-ext-dynamodb-appender
60 | logback-ext-cloudwatch-appender
61 | logback-ext-kinesis-appender
62 | logback-ext-lmax-appender
63 |
64 |
65 |
66 |
67 |
68 | org.eluder.logback
69 | logback-ext-core
70 | ${project.version}
71 |
72 |
73 | org.eluder.logback
74 | logback-ext-jackson
75 | ${project.version}
76 |
77 |
78 | org.eluder.logback
79 | logback-ext-aws-core
80 | ${project.version}
81 |
82 |
83 | org.eluder.logback
84 | logback-ext-sqs-appender
85 | ${project.version}
86 |
87 |
88 | org.eluder.logback
89 | logback-ext-sns-appender
90 | ${project.version}
91 |
92 |
93 | org.eluder.logback
94 | logback-ext-dynamodb-appender
95 | ${project.version}
96 |
97 |
98 | org.eluder.logback
99 | logback-ext-lmax-appender
100 | ${project.version}
101 |
102 |
103 | org.assertj
104 | assertj-core
105 | 2.1.0
106 |
107 |
108 | org.openjdk.jmh
109 | jmh-core
110 | ${jmh.version}
111 |
112 |
113 | org.openjdk.jmh
114 | jmh-generator-annprocess
115 | ${jmh.version}
116 |
117 |
118 |
119 |
120 |
121 |
122 | benchmark
123 |
124 |
125 |
126 |
127 | org.apache.maven.plugins
128 | maven-surefire-plugin
129 | ${maven.surefire.version}
130 |
131 |
132 | **/*Perf.java
133 |
134 |
135 | **/*Test.java
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
--------------------------------------------------------------------------------