001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache license, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the license for the specific language governing permissions and
015 * limitations under the license.
016 */
017package org.apache.log4j.bridge;
018
019import org.apache.log4j.Category;
020import org.apache.log4j.Level;
021import org.apache.log4j.spi.LocationInfo;
022import org.apache.log4j.spi.LoggingEvent;
023import org.apache.log4j.spi.ThrowableInformation;
024import org.apache.logging.log4j.core.LogEvent;
025import org.apache.logging.log4j.core.util.Loader;
026import org.apache.logging.log4j.core.util.Throwables;
027import org.apache.logging.log4j.spi.StandardLevel;
028import org.apache.logging.log4j.status.StatusLogger;
029import org.apache.logging.log4j.util.Strings;
030
031import java.lang.reflect.Method;
032import java.util.Map;
033import java.util.Set;
034
035/**
036 * Converts a Log4j 2 LogEvent into the components needed by a Log4j 1.x LoggingEvent.
037 * This class requires Log4j 2.
038 */
039public class LogEventAdapter extends LoggingEvent {
040
041    private static final long JVM_START_TIME = initStartTime();
042
043    private final LogEvent event;
044
045    public LogEventAdapter(LogEvent event) {
046        this.event = event;
047    }
048
049    /**
050     * Returns the time when the application started, in milliseconds
051     * elapsed since 01.01.1970.
052     * @return the time when the JVM started.
053     */
054    public static long getStartTime() {
055        return JVM_START_TIME;
056    }
057
058    /**
059     * Returns the result of {@code ManagementFactory.getRuntimeMXBean().getStartTime()},
060     * or the current system time if JMX is not available.
061     */
062    private static long initStartTime() {
063        // We'd like to call ManagementFactory.getRuntimeMXBean().getStartTime(),
064        // but Google App Engine throws a java.lang.NoClassDefFoundError
065        // "java.lang.management.ManagementFactory is a restricted class".
066        // The reflection is necessary because without it, Google App Engine
067        // will refuse to initialize this class.
068        try {
069            final Class<?> factoryClass = Loader.loadSystemClass("java.lang.management.ManagementFactory");
070            final Method getRuntimeMXBean = factoryClass.getMethod("getRuntimeMXBean");
071            final Object runtimeMXBean = getRuntimeMXBean.invoke(null);
072
073            final Class<?> runtimeMXBeanClass = Loader.loadSystemClass("java.lang.management.RuntimeMXBean");
074            final Method getStartTime = runtimeMXBeanClass.getMethod("getStartTime");
075            return (Long) getStartTime.invoke(runtimeMXBean);
076        } catch (final Throwable t) {
077            StatusLogger.getLogger().error("Unable to call ManagementFactory.getRuntimeMXBean().getStartTime(), "
078                    + "using system time for OnStartupTriggeringPolicy", t);
079            // We have little option but to declare "now" as the beginning of time.
080            return System.currentTimeMillis();
081        }
082    }
083
084    public LogEvent getEvent() {
085        return this.event;
086    }
087
088    /**
089     * Set the location information for this logging event. The collected
090     * information is cached for future use.
091     */
092    @Override
093    public LocationInfo getLocationInformation() {
094        return new LocationInfo(event.getSource());
095    }
096
097    /**
098     * Return the level of this event. Use this form instead of directly
099     * accessing the <code>level</code> field.
100     */
101    @Override
102    public Level getLevel() {
103        switch (StandardLevel.getStandardLevel(event.getLevel().intLevel())) {
104            case TRACE:
105                return Level.TRACE;
106            case DEBUG:
107                return Level.DEBUG;
108            case INFO:
109                return Level.INFO;
110            case WARN:
111                return Level.WARN;
112            case FATAL:
113                return Level.FATAL;
114            case OFF:
115                return Level.OFF;
116            case ALL:
117                return Level.ALL;
118            default:
119                return Level.ERROR;
120        }
121    }
122
123    /**
124     * Return the name of the logger. Use this form instead of directly
125     * accessing the <code>categoryName</code> field.
126     */
127    @Override
128    public String getLoggerName() {
129        return event.getLoggerName();
130    }
131
132    @Override
133    public long getTimeStamp() {
134        return event.getTimeMillis();
135    }
136
137    /**
138     * Gets the logger of the event.
139     */
140    @Override
141    public Category getLogger() {
142        return Category.getInstance(event.getLoggerName());
143    }
144
145    /*
146     Return the message for this logging event.
147    */
148    @Override
149    public Object getMessage() {
150        return event.getMessage();
151    }
152
153    /*
154     * This method returns the NDC for this event.
155     */
156    @Override
157    public String getNDC() {
158        return event.getContextStack().toString();
159    }
160
161    /*
162     Returns the context corresponding to the <code>key</code> parameter.
163     */
164    @Override
165    public Object getMDC(String key) {
166        if (event.getContextData() != null) {
167            return event.getContextData().getValue(key);
168        }
169        return null;
170    }
171
172    /**
173     * Obtain a copy of this thread's MDC prior to serialization or
174     * asynchronous logging.
175     */
176    @Override
177    public void getMDCCopy() {
178    }
179
180    @Override
181    public String getRenderedMessage() {
182        return event.getMessage().getFormattedMessage();
183    }
184
185    @Override
186    public String getThreadName() {
187        return event.getThreadName();
188    }
189
190    /**
191     * Returns the throwable information contained within this
192     * event. May be <code>null</code> if there is no such information.
193     *
194     * <p>Note that the {@link Throwable} object contained within a
195     * {@link ThrowableInformation} does not survive serialization.
196     *
197     * @since 1.1
198     */
199    @Override
200    public ThrowableInformation getThrowableInformation() {
201        if (event.getThrown() != null) {
202            return new ThrowableInformation(event.getThrown());
203        }
204        return null;
205    }
206
207    /**
208     * Return this event's throwable's string[] representaion.
209     */
210    @Override
211    public String[] getThrowableStrRep() {
212        if (event.getThrown() != null) {
213            return Throwables.toStringList(event.getThrown()).toArray(Strings.EMPTY_ARRAY);
214        }
215        return null;
216    }
217
218    @Override
219    public String getProperty(final String key) {
220        return event.getContextData().getValue(key);
221    }
222
223    @Override
224    public Set getPropertyKeySet() {
225        return event.getContextData().toMap().keySet();
226    }
227
228    @Override
229    public Map getProperties() {
230        return event.getContextData().toMap();
231    }
232}