View Javadoc
1   /*
2    * Copyright (c) 2001-2017, Zoltan Farkas All Rights Reserved.
3    *
4    * This library is free software; you can redistribute it and/or
5    * modify it under the terms of the GNU Lesser General Public
6    * License as published by the Free Software Foundation; either
7    * version 2.1 of the License, or (at your option) any later version.
8    *
9    * This library is distributed in the hope that it will be useful,
10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12   * GNU General Public License for more details.
13   *
14   * You should have received a copy of the GNU Lesser General Public
15   * License along with this program; if not, write to the Free Software
16   * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
17   *
18   * Additionally licensed with:
19   *
20   * Licensed under the Apache License, Version 2.0 (the "License");
21   * you may not use this file except in compliance with the License.
22   * You may obtain a copy of the License at
23   *
24   *      http://www.apache.org/licenses/LICENSE-2.0
25   *
26   * Unless required by applicable law or agreed to in writing, software
27   * distributed under the License is distributed on an "AS IS" BASIS,
28   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
29   * See the License for the specific language governing permissions and
30   * limitations under the License.
31   */
32  package org.spf4j.log;
33  
34  import com.google.common.collect.Maps;
35  import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
36  import java.time.Instant;
37  import java.util.ArrayList;
38  import java.util.Arrays;
39  import java.util.Collections;
40  import java.util.List;
41  import java.util.Map;
42  import java.util.Set;
43  import javax.annotation.Nonnull;
44  import javax.annotation.Nullable;
45  import org.slf4j.Marker;
46  import org.spf4j.base.StackSamples;
47  import org.spf4j.base.avro.Converters;
48  import static org.spf4j.base.avro.Converters.convert;
49  import org.spf4j.base.avro.LogRecord;
50  import org.spf4j.base.avro.StackSampleElement;
51  import org.spf4j.io.ObjectAppenderSupplier;
52  
53  /**
54   * @author Zoltan Farkas
55   */
56  public interface Slf4jLogRecord {
57  
58    /**
59     * @return all slf4j log arguments, getNrMessageArguments() message arguments followed by additional payload
60     * like exceptions, or other data.
61     */
62    @SuppressFBWarnings(value = "EI_EXPOSE_REP")
63    @Nonnull
64    Object[] getArguments();
65  
66    /**
67     * @return all extra arguments. (arguments that are not parameters for the message)
68     */
69    @Nonnull
70    Object[] getExtraArgumentsRaw();
71  
72    /**
73     * @return all non Throwable extra arguments.
74     */
75    @Nonnull
76    Object[] getExtraArguments();
77  
78    /**
79     * @return Throwable from extra arguments. If multiple, will return first will all others as suppressed.
80     */
81    @Nullable
82    Throwable getExtraThrowable();
83  
84    Level getLevel();
85  
86    String getLoggerName();
87  
88    @Nullable
89    Marker getMarker();
90  
91    @Nonnull
92    String getMessage();
93  
94    String getMessageFormat();
95  
96    int getNrMessageArguments();
97  
98    String getThreadName();
99  
100   long getTimeStamp();
101 
102   default Instant getTimeStampInstant() {
103     return Instant.ofEpochMilli(getTimeStamp());
104   }
105 
106   /**
107    * Indicates that this log record has been sent to the logging backend to persist.
108    *
109    * @return
110    */
111   boolean isLogged();
112 
113   void setIsLogged();
114 
115   void attach(Object obj);
116 
117   Set<Object> getAttachments();
118 
119   boolean hasAttachment(Object obj);
120 
121   static int compareByTimestamp(Slf4jLogRecord a, Slf4jLogRecord b) {
122     long timeDiff = a.getTimeStamp() - b.getTimeStamp();
123     if (timeDiff > 0) {
124       return 1;
125     } else if (timeDiff < 0) {
126       return -1;
127     } else {
128       return 0;
129     }
130   }
131 
132   @SuppressFBWarnings("WOC_WRITE_ONLY_COLLECTION_LOCAL")
133   default LogRecord toLogRecord(final String origin, final String ptraceId) {
134     java.lang.Throwable extraThrowable = this.getExtraThrowable();
135     Marker marker = this.getMarker();
136     Object[] extraArguments = this.getExtraArguments();
137     Map<String, Object> attribs = null;
138     List<Object> xArgs;
139     String traceId = ptraceId;
140     List<StackSampleElement> profiles = Collections.EMPTY_LIST;
141     if (extraArguments.length == 0) {
142       xArgs = Collections.emptyList();
143     } else {
144       int nrAttribs = 0;
145       for (Object obj : extraArguments) {
146         if (obj instanceof LogAttribute) {
147           LogAttribute la = (LogAttribute) obj;
148           String name = la.getName();
149           switch (name) {
150             case LogAttribute.ID_ATTR_NAME:
151               traceId = la.getValue().toString();
152               break;
153             case LogAttribute.PROFILE_SAMPLES_ATTR_NAME:
154               profiles = Converters.convert((StackSamples) la.getValue());
155               break;
156             default:
157               // nothiing to do.
158           }
159           nrAttribs++;
160         }
161       }
162       if (nrAttribs == 0) {
163         xArgs = Arrays.asList(extraArguments);
164       } else {
165         if (nrAttribs == extraArguments.length) {
166           xArgs = Collections.EMPTY_LIST;
167         } else {
168           xArgs = new ArrayList<>(extraArguments.length - nrAttribs);
169         }
170         attribs = Maps.newHashMapWithExpectedSize(nrAttribs + (marker == null ? 0 : 1));
171         for (Object obj : extraArguments) {
172           if (obj instanceof LogAttribute) {
173             LogAttribute la = (LogAttribute) obj;
174             String aName = la.getName();
175             switch (aName) {
176               case LogAttribute.ID_ATTR_NAME:
177               case LogAttribute.PROFILE_SAMPLES_ATTR_NAME:
178                 break;
179               default:
180                 attribs.put(aName, la.getValue());
181             }
182           } else {
183             xArgs.add(obj);
184           }
185         }
186         if (marker != null) {
187           attribs.put(marker.getName(), marker);
188         }
189       }
190     }
191     int nrMsgArgs = this.getNrMessageArguments();
192     List<String> messageArgs;
193     if (nrMsgArgs == 0) {
194         messageArgs = Collections.emptyList();
195     } else {
196         Object[] args = this.getArguments();
197         String[] ma = new String[nrMsgArgs];
198         for (int i = 0; i < nrMsgArgs; i++) {
199           Object arg = args[i];
200           if (arg == null) {
201             ma[i] = "null";
202           } else {
203             ma[i] = ObjectAppenderSupplier.TO_STRINGER.get(arg.getClass()).toString(arg);
204           }
205         }
206         messageArgs = Arrays.asList(ma);
207     }
208     return new LogRecord(origin, traceId, this.getLevel().getAvroLevel(),
209             Instant.ofEpochMilli(this.getTimeStamp()),
210             this.getLoggerName(), this.getThreadName(), this.getMessageFormat(),
211             messageArgs, xArgs,
212             attribs == null ? Collections.emptyMap() : attribs,
213             extraThrowable == null ? null : convert(extraThrowable), profiles);
214   }
215 
216 }