View Javadoc
1   /**
2    * Copyright (c) 2004-2021 QOS.ch
3    * All rights reserved.
4    *
5    * Permission is hereby granted, free  of charge, to any person obtaining
6    * a  copy  of this  software  and  associated  documentation files  (the
7    * "Software"), to  deal in  the Software without  restriction, including
8    * without limitation  the rights to  use, copy, modify,  merge, publish,
9    * distribute,  sublicense, and/or sell  copies of  the Software,  and to
10   * permit persons to whom the Software  is furnished to do so, subject to
11   * the following conditions:
12   *
13   * The  above  copyright  notice  and  this permission  notice  shall  be
14   * included in all copies or substantial portions of the Software.
15   *
16   * THE  SOFTWARE IS  PROVIDED  "AS  IS", WITHOUT  WARRANTY  OF ANY  KIND,
17   * EXPRESS OR  IMPLIED, INCLUDING  BUT NOT LIMITED  TO THE  WARRANTIES OF
18   * MERCHANTABILITY,    FITNESS    FOR    A   PARTICULAR    PURPOSE    AND
19   * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20   * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21   * OF CONTRACT, TORT OR OTHERWISE,  ARISING FROM, OUT OF OR IN CONNECTION
22   * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23   *
24   */
25  package org.slf4j.jdk.platform.logging;
26  
27  import static java.util.Objects.requireNonNull;
28  
29  import java.util.MissingResourceException;
30  import java.util.ResourceBundle;
31  
32  import org.slf4j.Logger;
33  import org.slf4j.spi.CallerBoundaryAware;
34  import org.slf4j.spi.LoggingEventBuilder;
35  
36  /**
37   * Adapts {@link Logger} to {@link System.Logger}.
38   * @since 2.0.0
39   */
40  class SLF4JPlatformLogger implements System.Logger {
41  
42      static private final String PRESUMED_CALLER_BOUNDARY = System.Logger.class.getName();
43                      
44      private final Logger slf4jLogger;
45  
46      public SLF4JPlatformLogger(Logger logger) {
47          this.slf4jLogger = requireNonNull(logger);
48      }
49  
50      @Override
51      public String getName() {
52          return slf4jLogger.getName();
53      }
54  
55      // The fact that non loggable levels (in java.lang.System.Logger.Level)
56      // such as ALL and OFF leak into the public interface is quite a pity.
57  
58      @Override
59      public boolean isLoggable(Level jplLevel) {
60          if (jplLevel == Level.ALL)
61              return true;
62          if (jplLevel == Level.OFF)
63              return true;
64  
65          org.slf4j.event.Level slf4jLevel = jplLevelToSLF4JLevel(jplLevel);
66  
67          return slf4jLogger.isEnabledForLevel(slf4jLevel);
68      }
69  
70  
71      /**
72       * Transform a {@link Level} to {@link org.slf4j.event.Level}.
73       * 
74       * This method assumes that Level.ALL or Level.OFF never reach this method.
75       * 
76       * @param jplLevel
77       * @return
78       */
79      private org.slf4j.event.Level jplLevelToSLF4JLevel(Level jplLevel) {
80          switch (jplLevel) {
81          case TRACE:
82              return org.slf4j.event.Level.TRACE;
83          case DEBUG:
84              return org.slf4j.event.Level.DEBUG;
85          case INFO:
86              return org.slf4j.event.Level.INFO;
87          case WARNING:
88              return org.slf4j.event.Level.WARN;
89          case ERROR:
90              return org.slf4j.event.Level.ERROR;
91          default:
92              reportUnknownLevel(jplLevel);
93              return null;
94          }
95      }
96  
97      @Override
98      public void log(Level jplLevel, ResourceBundle bundle, String msg, Throwable thrown) {
99          log(jplLevel, bundle, msg, thrown, (Object[]) null);
100     }
101 
102     @Override
103     public void log(Level jplLevel, ResourceBundle bundle, String format, Object... params) {
104         log(jplLevel, bundle, format, null, params);
105     }
106 
107     /**
108      * Single point of processing taking all possible paramets.
109      * 
110      * @param jplLevel 
111      * @param bundle
112      * @param msg
113      * @param thrown
114      * @param params
115      */
116     private void log(Level jplLevel, ResourceBundle bundle, String msg, Throwable thrown, Object... params) {
117         if (jplLevel == Level.OFF)
118             return;
119 
120         if (jplLevel == Level.ALL) {
121             performLog(org.slf4j.event.Level.TRACE, bundle, msg, thrown, params);
122             return;
123         }
124 
125         org.slf4j.event.Level slf4jLevel = jplLevelToSLF4JLevel(jplLevel);
126         boolean isEnabled = slf4jLogger.isEnabledForLevel(slf4jLevel);
127 
128         if (isEnabled) {
129             performLog(slf4jLevel, bundle, msg, thrown, params);
130         }
131     }
132 
133     private void performLog(org.slf4j.event.Level slf4jLevel, ResourceBundle bundle, String msg, Throwable thrown, Object... params) {
134         String message = getResourceStringOrMessage(bundle, msg);
135         LoggingEventBuilder leb = slf4jLogger.makeLoggingEventBuilder(slf4jLevel);
136         if (thrown != null) {
137             leb = leb.setCause(thrown);
138         }
139         if (params != null && params.length > 0) {
140             // add the arguments to the logging event for possible processing by the backend
141             for (Object p : params) {
142                 leb = leb.addArgument(p);
143             }
144             // The JDK uses a different formatting convention. We must invoke it now.
145             message = String.format(message, params);
146         }
147         if (leb instanceof CallerBoundaryAware) {
148             CallerBoundaryAware cba = (CallerBoundaryAware) leb;
149             cba.setCallerBoundary(PRESUMED_CALLER_BOUNDARY);
150         }
151         leb.log(message);
152     }
153 
154     private void reportUnknownLevel(Level jplLevel) {
155         String message = "Unknown log level [" + jplLevel + "]";
156         IllegalArgumentException iae = new IllegalArgumentException(message);
157         org.slf4j.helpers.Util.report("Unsupported log level", iae);
158     }
159 
160     private static String getResourceStringOrMessage(ResourceBundle bundle, String msg) {
161         if (bundle == null || msg == null)
162             return msg;
163         // ResourceBundle::getString throws:
164         //
165         // * NullPointerException for null keys
166         // * ClassCastException if the message is no string
167         // * MissingResourceException if there is no message for the key
168         //
169         // Handle all of these cases here to avoid log-related exceptions from crashing the JVM.
170         try {
171             return bundle.getString(msg);
172         } catch (MissingResourceException ex) {
173             return msg;
174         } catch (ClassCastException ex) {
175             return bundle.getObject(msg).toString();
176         }
177     }
178 
179 }