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.text.MessageFormat;
30 import java.util.MissingResourceException;
31 import java.util.ResourceBundle;
32 
33 import org.slf4j.Logger;
34 import org.slf4j.helpers.Reporter;
35 import org.slf4j.spi.CallerBoundaryAware;
36 import org.slf4j.spi.LoggingEventBuilder;
37 
38 /**
39  * Adapts {@link Logger} to {@link System.Logger}.
40  * @since 2.0.0
41  */
42 class SLF4JPlatformLogger implements System.Logger {
43 
44     static private final String PRESUMED_CALLER_BOUNDARY = System.Logger.class.getName();
45 
46     private final Logger slf4jLogger;
47 
SLF4JPlatformLogger(Logger logger)48     public SLF4JPlatformLogger(Logger logger) {
49         this.slf4jLogger = requireNonNull(logger);
50     }
51 
52     @Override
getName()53     public String getName() {
54         return slf4jLogger.getName();
55     }
56 
57     // The fact that non loggable levels (in java.lang.System.Logger.Level)
58     // such as ALL and OFF leak into the public interface is quite a pity.
59 
60     @Override
isLoggable(Level jplLevel)61     public boolean isLoggable(Level jplLevel) {
62         if (jplLevel == Level.ALL)
63             return true;
64         if (jplLevel == Level.OFF)
65             return true;
66 
67         org.slf4j.event.Level slf4jLevel = jplLevelToSLF4JLevel(jplLevel);
68 
69         return slf4jLogger.isEnabledForLevel(slf4jLevel);
70     }
71 
72 
73     /**
74      * Transform a {@link Level} to {@link org.slf4j.event.Level}.
75      *
76      * This method assumes that Level.ALL or Level.OFF never reach this method.
77      *
78      * @param jplLevel
79      * @return
80      */
jplLevelToSLF4JLevel(Level jplLevel)81     private org.slf4j.event.Level jplLevelToSLF4JLevel(Level jplLevel) {
82         switch (jplLevel) {
83         case TRACE:
84             return org.slf4j.event.Level.TRACE;
85         case DEBUG:
86             return org.slf4j.event.Level.DEBUG;
87         case INFO:
88             return org.slf4j.event.Level.INFO;
89         case WARNING:
90             return org.slf4j.event.Level.WARN;
91         case ERROR:
92             return org.slf4j.event.Level.ERROR;
93         default:
94             reportUnknownLevel(jplLevel);
95             return null;
96         }
97     }
98 
99     @Override
log(Level jplLevel, ResourceBundle bundle, String msg, Throwable thrown)100     public void log(Level jplLevel, ResourceBundle bundle, String msg, Throwable thrown) {
101         log(jplLevel, bundle, msg, thrown, (Object[]) null);
102     }
103 
104     @Override
log(Level jplLevel, ResourceBundle bundle, String format, Object... params)105     public void log(Level jplLevel, ResourceBundle bundle, String format, Object... params) {
106         log(jplLevel, bundle, format, null, params);
107     }
108 
109     /**
110      * Single point of processing taking all possible parameters.
111      *
112      * @param jplLevel
113      * @param bundle
114      * @param msg
115      * @param thrown
116      * @param params
117      */
log(Level jplLevel, ResourceBundle bundle, String msg, Throwable thrown, Object... params)118     private void log(Level jplLevel, ResourceBundle bundle, String msg, Throwable thrown, Object... params) {
119         if (jplLevel == Level.OFF)
120             return;
121 
122         if (jplLevel == Level.ALL) {
123             performLog(org.slf4j.event.Level.TRACE, bundle, msg, thrown, params);
124             return;
125         }
126 
127         org.slf4j.event.Level slf4jLevel = jplLevelToSLF4JLevel(jplLevel);
128         boolean isEnabled = slf4jLogger.isEnabledForLevel(slf4jLevel);
129 
130         if (isEnabled) {
131             performLog(slf4jLevel, bundle, msg, thrown, params);
132         }
133     }
134 
performLog(org.slf4j.event.Level slf4jLevel, ResourceBundle bundle, String msg, Throwable thrown, Object... params)135     private void performLog(org.slf4j.event.Level slf4jLevel, ResourceBundle bundle, String msg, Throwable thrown, Object... params) {
136         String message = getResourceStringOrMessage(bundle, msg);
137         LoggingEventBuilder leb = slf4jLogger.makeLoggingEventBuilder(slf4jLevel);
138         if (thrown != null) {
139             leb = leb.setCause(thrown);
140         }
141         if (params != null && params.length > 0) {
142             // add the arguments to the logging event for possible processing by the backend
143             for (Object p : params) {
144                 leb = leb.addArgument(p);
145             }
146             // The JDK uses a different formatting convention. We must invoke it now.
147             message = MessageFormat.format(message, params);
148         }
149         if (leb instanceof CallerBoundaryAware) {
150             CallerBoundaryAware cba = (CallerBoundaryAware) leb;
151             cba.setCallerBoundary(PRESUMED_CALLER_BOUNDARY);
152         }
153         leb.log(message);
154     }
155 
reportUnknownLevel(Level jplLevel)156     private void reportUnknownLevel(Level jplLevel) {
157         String message = "Unknown log level [" + jplLevel + "]";
158         IllegalArgumentException iae = new IllegalArgumentException(message);
159         Reporter.error("Unsupported log level", iae);
160     }
161 
getResourceStringOrMessage(ResourceBundle bundle, String msg)162     private static String getResourceStringOrMessage(ResourceBundle bundle, String msg) {
163         if (bundle == null || msg == null)
164             return msg;
165         // ResourceBundle::getString throws:
166         //
167         // * NullPointerException for null keys
168         // * ClassCastException if the message is no string
169         // * MissingResourceException if there is no message for the key
170         //
171         // Handle all of these cases here to avoid log-related exceptions from crashing the JVM.
172         try {
173             return bundle.getString(msg);
174         } catch (MissingResourceException ex) {
175             return msg;
176         } catch (ClassCastException ex) {
177             return bundle.getObject(msg).toString();
178         }
179     }
180 
181 }
182