GUACAMOLE-2181: Include extension information and line continuations in log messages.
This commit is contained in:
9 files changed
+768
-28
No files matched your search
@@ -1 +1,2 @@
|
|||||||
src/main/frontend/dist/**/*
|
src/main/frontend/dist/**/*
|
||||||
|
src/main/resources/META-INF/services/*
|
||||||
+78
-11
@@ -23,12 +23,14 @@ import java.util.Set;
|
|||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import org.apache.guacamole.GuacamoleClientException;
|
import org.apache.guacamole.GuacamoleClientException;
|
||||||
import org.apache.guacamole.GuacamoleException;
|
import org.apache.guacamole.GuacamoleException;
|
||||||
|
import org.apache.guacamole.log.LogModule;
|
||||||
import org.apache.guacamole.net.auth.AuthenticatedUser;
|
import org.apache.guacamole.net.auth.AuthenticatedUser;
|
||||||
import org.apache.guacamole.net.auth.AuthenticationProvider;
|
import org.apache.guacamole.net.auth.AuthenticationProvider;
|
||||||
import org.apache.guacamole.net.auth.Credentials;
|
import org.apache.guacamole.net.auth.Credentials;
|
||||||
import org.apache.guacamole.net.auth.UserContext;
|
import org.apache.guacamole.net.auth.UserContext;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.slf4j.MDC;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides a safe wrapper around an AuthenticationProvider subclass, such that
|
* Provides a safe wrapper around an AuthenticationProvider subclass, such that
|
||||||
@@ -40,7 +42,7 @@ public class AuthenticationProviderFacade implements AuthenticationProvider {
|
|||||||
/**
|
/**
|
||||||
* Logger for this class.
|
* Logger for this class.
|
||||||
*/
|
*/
|
||||||
private Logger logger = LoggerFactory.getLogger(AuthenticationProviderFacade.class);
|
private final Logger logger = LoggerFactory.getLogger(AuthenticationProviderFacade.class);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The underlying authentication provider, or null if the authentication
|
* The underlying authentication provider, or null if the authentication
|
||||||
@@ -58,6 +60,12 @@ public class AuthenticationProviderFacade implements AuthenticationProvider {
|
|||||||
*/
|
*/
|
||||||
private final Set<String> tolerateFailures;
|
private final Set<String> tolerateFailures;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The logging context that should be provided to SLF4J's MDC for all log
|
||||||
|
* messages originating from the underlying authentication provider.
|
||||||
|
*/
|
||||||
|
private final String logContext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The identifier to provide for the underlying authentication provider if
|
* The identifier to provide for the underlying authentication provider if
|
||||||
* the authentication provider could not be loaded.
|
* the authentication provider could not be loaded.
|
||||||
@@ -86,9 +94,16 @@ public class AuthenticationProviderFacade implements AuthenticationProvider {
|
|||||||
public AuthenticationProviderFacade(
|
public AuthenticationProviderFacade(
|
||||||
Class<? extends AuthenticationProvider> authProviderClass,
|
Class<? extends AuthenticationProvider> authProviderClass,
|
||||||
Set<String> tolerateFailures) {
|
Set<String> tolerateFailures) {
|
||||||
|
|
||||||
this.tolerateFailures = tolerateFailures;
|
this.tolerateFailures = tolerateFailures;
|
||||||
this.authProvider = ProviderFactory.newInstance("authentication provider",
|
this.authProvider = ProviderFactory.newInstance("authentication provider",
|
||||||
authProviderClass);
|
authProviderClass);
|
||||||
|
|
||||||
|
if (this.authProvider != null)
|
||||||
|
this.logContext = LogModule.getMDCContext(authProvider);
|
||||||
|
else
|
||||||
|
this.logContext = LogModule.MDC_CONTEXT_ROOT;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -115,7 +130,13 @@ public class AuthenticationProviderFacade implements AuthenticationProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Delegate to underlying auth provider
|
// Delegate to underlying auth provider
|
||||||
return authProvider.getResource();
|
try {
|
||||||
|
MDC.put(LogModule.MDC_CONTEXT_KEY, logContext);
|
||||||
|
return authProvider.getResource();
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
MDC.put(LogModule.MDC_CONTEXT_KEY, LogModule.MDC_CONTEXT_ROOT);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,7 +148,13 @@ public class AuthenticationProviderFacade implements AuthenticationProvider {
|
|||||||
return credentials;
|
return credentials;
|
||||||
|
|
||||||
// Delegate to underlying auth provider
|
// Delegate to underlying auth provider
|
||||||
return authProvider.updateCredentials(credentials);
|
try {
|
||||||
|
MDC.put(LogModule.MDC_CONTEXT_KEY, logContext);
|
||||||
|
return authProvider.updateCredentials(credentials);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
MDC.put(LogModule.MDC_CONTEXT_KEY, LogModule.MDC_CONTEXT_ROOT);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,6 +226,7 @@ public class AuthenticationProviderFacade implements AuthenticationProvider {
|
|||||||
|
|
||||||
// Delegate to underlying auth provider
|
// Delegate to underlying auth provider
|
||||||
try {
|
try {
|
||||||
|
MDC.put(LogModule.MDC_CONTEXT_KEY, logContext);
|
||||||
return authProvider.authenticateUser(credentials);
|
return authProvider.authenticateUser(credentials);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,6 +252,10 @@ public class AuthenticationProviderFacade implements AuthenticationProvider {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
finally {
|
||||||
|
MDC.put(LogModule.MDC_CONTEXT_KEY, LogModule.MDC_CONTEXT_ROOT);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -239,7 +271,13 @@ public class AuthenticationProviderFacade implements AuthenticationProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Delegate to underlying auth provider
|
// Delegate to underlying auth provider
|
||||||
return authProvider.updateAuthenticatedUser(authenticatedUser, credentials);
|
try {
|
||||||
|
MDC.put(LogModule.MDC_CONTEXT_KEY, logContext);
|
||||||
|
return authProvider.updateAuthenticatedUser(authenticatedUser, credentials);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
MDC.put(LogModule.MDC_CONTEXT_KEY, LogModule.MDC_CONTEXT_ROOT);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,6 +295,7 @@ public class AuthenticationProviderFacade implements AuthenticationProvider {
|
|||||||
|
|
||||||
// Delegate to underlying auth provider
|
// Delegate to underlying auth provider
|
||||||
try {
|
try {
|
||||||
|
MDC.put(LogModule.MDC_CONTEXT_KEY, logContext);
|
||||||
return authProvider.getUserContext(authenticatedUser);
|
return authProvider.getUserContext(authenticatedUser);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -282,6 +321,10 @@ public class AuthenticationProviderFacade implements AuthenticationProvider {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
finally {
|
||||||
|
MDC.put(LogModule.MDC_CONTEXT_KEY, LogModule.MDC_CONTEXT_ROOT);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -298,8 +341,14 @@ public class AuthenticationProviderFacade implements AuthenticationProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Delegate to underlying auth provider
|
// Delegate to underlying auth provider
|
||||||
return authProvider.updateUserContext(context, authenticatedUser, credentials);
|
try {
|
||||||
|
MDC.put(LogModule.MDC_CONTEXT_KEY, logContext);
|
||||||
|
return authProvider.updateUserContext(context, authenticatedUser, credentials);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
MDC.put(LogModule.MDC_CONTEXT_KEY, LogModule.MDC_CONTEXT_ROOT);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -312,7 +361,13 @@ public class AuthenticationProviderFacade implements AuthenticationProvider {
|
|||||||
return context;
|
return context;
|
||||||
|
|
||||||
// Delegate to underlying auth provider
|
// Delegate to underlying auth provider
|
||||||
return authProvider.decorate(context, authenticatedUser, credentials);
|
try {
|
||||||
|
MDC.put(LogModule.MDC_CONTEXT_KEY, logContext);
|
||||||
|
return authProvider.decorate(context, authenticatedUser, credentials);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
MDC.put(LogModule.MDC_CONTEXT_KEY, LogModule.MDC_CONTEXT_ROOT);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -326,15 +381,27 @@ public class AuthenticationProviderFacade implements AuthenticationProvider {
|
|||||||
return context;
|
return context;
|
||||||
|
|
||||||
// Delegate to underlying auth provider
|
// Delegate to underlying auth provider
|
||||||
return authProvider.redecorate(decorated, context,
|
try {
|
||||||
authenticatedUser, credentials);
|
MDC.put(LogModule.MDC_CONTEXT_KEY, logContext);
|
||||||
|
return authProvider.redecorate(decorated, context,
|
||||||
|
authenticatedUser, credentials);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
MDC.put(LogModule.MDC_CONTEXT_KEY, LogModule.MDC_CONTEXT_ROOT);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void shutdown() {
|
public void shutdown() {
|
||||||
if (authProvider != null)
|
try {
|
||||||
authProvider.shutdown();
|
MDC.put(LogModule.MDC_CONTEXT_KEY, logContext);
|
||||||
|
if (authProvider != null)
|
||||||
|
authProvider.shutdown();
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
MDC.put(LogModule.MDC_CONTEXT_KEY, LogModule.MDC_CONTEXT_ROOT);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,267 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.guacamole.log;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HashMap implementation that uses copy-on-write to allow efficient cloning.
|
||||||
|
* When an instance of ForkableHashMap has been copied using {@link #fork()},
|
||||||
|
* the parent and child both continue to refer to the same copy of their
|
||||||
|
* underlying data. If the parent (or child) is modified, only then does the
|
||||||
|
* parent (or child) create an independent copy of that underlying data.
|
||||||
|
* <p>
|
||||||
|
* For simplicity, this implementation DOES NOT permit modification via the
|
||||||
|
* collections/sets returned by {@link #values()}, {@link #keySet()}, or
|
||||||
|
* {@link #entrySet()}.
|
||||||
|
* <p>
|
||||||
|
* As with the standard HashMap, this implementation IS NOT threadsafe.
|
||||||
|
*
|
||||||
|
* @param <K>
|
||||||
|
* The type of key used to store/retrieve values within this map.
|
||||||
|
*
|
||||||
|
* @param <V>
|
||||||
|
* The type of values stored by this map.
|
||||||
|
*/
|
||||||
|
public class ForkableHashMap<K, V> implements Map<K, V> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The underlying content of this ForkableHashMap. This content is shared
|
||||||
|
* with at least one other ForkableHashMap if {@link #shared} is true, and
|
||||||
|
* is an independent copy that may safely be modified if {@link #shared} is
|
||||||
|
* false.
|
||||||
|
*/
|
||||||
|
private Map<K, V> content;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the Map referenced by {@link #content} is shared with at least
|
||||||
|
* one other ForkableHashMap.
|
||||||
|
*/
|
||||||
|
private boolean shared;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new, empty ForkableHashMap.
|
||||||
|
*/
|
||||||
|
public ForkableHashMap() {
|
||||||
|
this.content = new HashMap<>();
|
||||||
|
this.shared = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new ForkableHashMap that contains an independent copy of the
|
||||||
|
* entries in the provided map.
|
||||||
|
* <p>
|
||||||
|
* This is not a deep copy; only the references to keys and values are
|
||||||
|
* copied.
|
||||||
|
*
|
||||||
|
* @param map
|
||||||
|
* The map to copy.
|
||||||
|
*/
|
||||||
|
public ForkableHashMap(Map<K, V> map) {
|
||||||
|
this.content = new HashMap<>(map);
|
||||||
|
this.shared = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new ForkableHashMap that shares the content of the given
|
||||||
|
* ForkableHashMap. It is the responsibility of the caller to ensure that
|
||||||
|
* the given ForkableHashMap has its content marked as shared.
|
||||||
|
*
|
||||||
|
* @param parent
|
||||||
|
* The ForkableHashMap to lazily copy.
|
||||||
|
*/
|
||||||
|
private ForkableHashMap(ForkableHashMap<K, V> parent) {
|
||||||
|
this.content = parent.content;
|
||||||
|
this.shared = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new ForkableHashMap that shares the content of this map. No
|
||||||
|
* actual copy operation will be performed until this map or the new map
|
||||||
|
* are modified.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* A new ForkableHashMap that lazily shares the content of this map.
|
||||||
|
*/
|
||||||
|
public ForkableHashMap<K, V> fork() {
|
||||||
|
shared = true;
|
||||||
|
return new ForkableHashMap<>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the map providing content for this ForkableHashMap while
|
||||||
|
* ensuring that map is safe to modify. If the map is shared with another
|
||||||
|
* ForkableHashMap, the map is first replaced with an independent copy.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* The map providing content for this ForkableHashMap, which shall be
|
||||||
|
* safe to modify after this function returns.
|
||||||
|
*/
|
||||||
|
private Map<K, V> writableContent() {
|
||||||
|
|
||||||
|
// Transform into independent copy if other instances of ForkableHashMap
|
||||||
|
// share the same underlying data
|
||||||
|
if (shared) {
|
||||||
|
content = new HashMap<>(content);
|
||||||
|
shared = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return content;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return content.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return content.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsKey(Object o) {
|
||||||
|
return content.containsKey(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsValue(Object o) {
|
||||||
|
return content.containsValue(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V get(Object o) {
|
||||||
|
return content.get(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V put(K k, V v) {
|
||||||
|
return writableContent().put(k, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V remove(Object o) {
|
||||||
|
return writableContent().remove(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void putAll(Map<? extends K, ? extends V> map) {
|
||||||
|
writableContent().putAll(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
writableContent().clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<K> keySet() {
|
||||||
|
return Collections.unmodifiableSet(content.keySet());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<V> values() {
|
||||||
|
return Collections.unmodifiableCollection(content.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Entry<K, V>> entrySet() {
|
||||||
|
return Collections.unmodifiableSet(content.entrySet());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
return content.equals(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return content.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V getOrDefault(Object key, V defaultValue) {
|
||||||
|
return content.getOrDefault(key, defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void forEach(BiConsumer<? super K, ? super V> action) {
|
||||||
|
content.forEach(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
|
||||||
|
writableContent().replaceAll(function);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V putIfAbsent(K key, V value) {
|
||||||
|
return writableContent().putIfAbsent(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean remove(Object key, Object value) {
|
||||||
|
return writableContent().remove(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean replace(K key, V oldValue, V newValue) {
|
||||||
|
return writableContent().replace(key, oldValue, newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V replace(K key, V value) {
|
||||||
|
return writableContent().replace(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {
|
||||||
|
return content.computeIfAbsent(key, mappingFunction);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
|
||||||
|
return content.computeIfPresent(key, remappingFunction);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
|
||||||
|
return content.compute(key, remappingFunction);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
|
||||||
|
return writableContent().merge(key, value, remappingFunction);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return content.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+109
@@ -0,0 +1,109 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.guacamole.log;
|
||||||
|
|
||||||
|
import ch.qos.logback.classic.LoggerContext;
|
||||||
|
import ch.qos.logback.classic.util.ContextInitializer;
|
||||||
|
import ch.qos.logback.core.joran.spi.JoranException;
|
||||||
|
import org.slf4j.ILoggerFactory;
|
||||||
|
import org.slf4j.IMarkerFactory;
|
||||||
|
import org.slf4j.helpers.BasicMarkerFactory;
|
||||||
|
import org.slf4j.spi.MDCAdapter;
|
||||||
|
import org.slf4j.spi.SLF4JServiceProvider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SLF4JServiceProvider that overrides the default behavior of Logback's
|
||||||
|
* service provider, forcing the use of a custom MDCAdapter.
|
||||||
|
*/
|
||||||
|
public class GuacamoleLogbackServiceProvider implements SLF4JServiceProvider {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The highest version of SLF4J that this service provider can support.
|
||||||
|
* This is intentionally not actually a released version, but a reasonable
|
||||||
|
* upper bound. This value is identical to the value used by Logback.
|
||||||
|
*/
|
||||||
|
private static final String SLF4J_API_MAX_VERSION = "2.0.99";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the LoggerContext maintained by this service provider.
|
||||||
|
*/
|
||||||
|
private static final String LOGGER_CONTEXT_NAME = "guacamole";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The singleton instance of the LoggerContext maintained by this service
|
||||||
|
* provider. The LoggerContext is actually the LoggerFactory.
|
||||||
|
*/
|
||||||
|
private LoggerContext context;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The singleton instance of the MDCAdapter implementation that should be
|
||||||
|
* used by Logback and SLF4J. The custom implementation provided here
|
||||||
|
* allows the context map to be inherited by child threads.
|
||||||
|
*/
|
||||||
|
private final MDCAdapter mdcAdapter = new InheritableMDCAdapter();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The singleton MarkerFactory instance maintained by this service
|
||||||
|
* provider.
|
||||||
|
*/
|
||||||
|
private final IMarkerFactory markerFactory = new BasicMarkerFactory();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize() {
|
||||||
|
|
||||||
|
context = new LoggerContext();
|
||||||
|
context.setName(LOGGER_CONTEXT_NAME);
|
||||||
|
context.setMDCAdapter(mdcAdapter);
|
||||||
|
|
||||||
|
// Perform basic init (prior to loading any Guacamole-specific
|
||||||
|
// configuration, which is loaded by the LogModule)
|
||||||
|
try {
|
||||||
|
new ContextInitializer(context).autoConfig();
|
||||||
|
}
|
||||||
|
catch (JoranException | RuntimeException | Error e) {
|
||||||
|
System.err.println("Logging system could not be initialized: " + e.getMessage());
|
||||||
|
e.printStackTrace(System.err);
|
||||||
|
}
|
||||||
|
|
||||||
|
context.start();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MDCAdapter getMDCAdapter() {
|
||||||
|
return mdcAdapter;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ILoggerFactory getLoggerFactory() {
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IMarkerFactory getMarkerFactory() {
|
||||||
|
return markerFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getRequestedApiVersion() {
|
||||||
|
return SLF4J_API_MAX_VERSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,155 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.guacamole.log;
|
||||||
|
|
||||||
|
import java.util.Deque;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.slf4j.spi.MDCAdapter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MDCAdapter implementation that allows individual string context values to be
|
||||||
|
* inherited by child threads. Deque context values (as manipulated with
|
||||||
|
* {@link #pushByKey(java.lang.String, java.lang.String)}, etc.) are not
|
||||||
|
* inherited.
|
||||||
|
*/
|
||||||
|
public class InheritableMDCAdapter implements MDCAdapter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MDC context map that is automatically inherited by child threads.
|
||||||
|
*/
|
||||||
|
private final InheritableThreadLocal<ForkableHashMap<String, String>> context =
|
||||||
|
new InheritableThreadLocal<ForkableHashMap<String, String>>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ForkableHashMap<String, String> initialValue() {
|
||||||
|
return new ForkableHashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ForkableHashMap<String, String> childValue(ForkableHashMap<String, String> parentValue) {
|
||||||
|
// This function is invoked by the JVM for every thread created,
|
||||||
|
// so simply creating a new copy is expensive. We instead create a
|
||||||
|
// copy-on-write reference.
|
||||||
|
return parentValue.fork();
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MDC deque map that is NOT automatically inherited by child threads. We
|
||||||
|
* choose not to inherit the deque map as that would result in pushed values
|
||||||
|
* possibly never getting popped.
|
||||||
|
* <p>
|
||||||
|
* NOTE: The Deques have no relation to the single values maintained using
|
||||||
|
* {@link #put(java.lang.String, java.lang.String)} and retrieved using
|
||||||
|
* {@link #get(java.lang.String)} which are intentionally stored
|
||||||
|
* separately in {@link #context}. This matches the behavior of the Logback
|
||||||
|
* implementation of MDC.
|
||||||
|
*/
|
||||||
|
private final ThreadLocal<Map<String, Deque<String>>> contextDeques =
|
||||||
|
new ThreadLocal<Map<String, Deque<String>>>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Map<String, Deque<String>> initialValue() {
|
||||||
|
return new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void put(String key, String value) {
|
||||||
|
context.get().put(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String get(String key) {
|
||||||
|
return context.get().get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove(String key) {
|
||||||
|
context.get().remove(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
context.get().clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, String> getCopyOfContextMap() {
|
||||||
|
// This function is invoked by Logback for every log message produced,
|
||||||
|
// so simply copying the context map is expensive. We instead create a
|
||||||
|
// copy-on-write reference to the context map.
|
||||||
|
return context.get().fork();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setContextMap(Map<String, String> contextMap) {
|
||||||
|
context.set(new ForkableHashMap<>(contextMap));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Deque of pushed values associated with the given key, as
|
||||||
|
* required for {@link #pushByKey(java.lang.String, java.lang.String)} and
|
||||||
|
* {@link #popByKey(java.lang.String)}. If no Deque yet exists for the
|
||||||
|
* given key, it is first created.
|
||||||
|
*
|
||||||
|
* @param key
|
||||||
|
* The key to retrieve the Deque for.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* The Deque of pushed values for the given key.
|
||||||
|
*/
|
||||||
|
private Deque<String> getDeque(String key) {
|
||||||
|
|
||||||
|
Deque<String> values = contextDeques.get().get(key);
|
||||||
|
if (values == null) {
|
||||||
|
values = new LinkedList<>();
|
||||||
|
contextDeques.get().put(key, values);
|
||||||
|
}
|
||||||
|
|
||||||
|
return values;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void pushByKey(String key, String value) {
|
||||||
|
getDeque(key).push(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String popByKey(String key) {
|
||||||
|
return getDeque(key).pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Deque<String> getCopyOfDequeByKey(String key) {
|
||||||
|
return new LinkedList<>(getDeque(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clearDequeByKey(String key) {
|
||||||
|
getDeque(key).clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -56,14 +56,14 @@ public enum LogLevel {
|
|||||||
* messages, while verbose, will not affect performance.
|
* messages, while verbose, will not affect performance.
|
||||||
*/
|
*/
|
||||||
@PropertyValue("debug")
|
@PropertyValue("debug")
|
||||||
DEBUG("debug"),
|
DEBUG("debug", "${guac_verbose_pattern}"),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Informational messages that may be useful for debugging, but which are
|
* Informational messages that may be useful for debugging, but which are
|
||||||
* so low-level that they may affect performance.
|
* so low-level that they may affect performance.
|
||||||
*/
|
*/
|
||||||
@PropertyValue("trace")
|
@PropertyValue("trace")
|
||||||
TRACE("trace");
|
TRACE("trace", "${guac_verbose_pattern}");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Format string whose sole format argument is a String containing the
|
* Format string whose sole format argument is a String containing the
|
||||||
@@ -77,7 +77,7 @@ public enum LogLevel {
|
|||||||
+ " <!-- Default appender -->\n"
|
+ " <!-- Default appender -->\n"
|
||||||
+ " <appender name=\"GUAC-DEFAULT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n"
|
+ " <appender name=\"GUAC-DEFAULT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n"
|
||||||
+ " <encoder>\n"
|
+ " <encoder>\n"
|
||||||
+ " <pattern>%%d{HH:mm:ss.SSS} [%%thread] %%-5level %%logger{36} - %%msg%%n</pattern>\n"
|
+ " <pattern>%s</pattern>\n"
|
||||||
+ " </encoder>\n"
|
+ " </encoder>\n"
|
||||||
+ " </appender>\n"
|
+ " </appender>\n"
|
||||||
+ "\n"
|
+ "\n"
|
||||||
@@ -102,9 +102,10 @@ public enum LogLevel {
|
|||||||
private final byte[] logbackConfig;
|
private final byte[] logbackConfig;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new LogLevel with the given names. The pair of names provided
|
* Creates a new LogLevel with the given names and pattern. The pair of
|
||||||
* correspond to the name used within Guacamole's configuration and the
|
* names provided correspond to the name used within Guacamole's
|
||||||
* name used within Logback's configuration.
|
* configuration and the name used within Logback's configuration, while
|
||||||
|
* the pattern determines how Logback will convert log messages to text.
|
||||||
*
|
*
|
||||||
* @param canonicalName
|
* @param canonicalName
|
||||||
* The name that should be used for this log level when configuring
|
* The name that should be used for this log level when configuring
|
||||||
@@ -113,10 +114,14 @@ public enum LogLevel {
|
|||||||
* @param logbackLogLevel
|
* @param logbackLogLevel
|
||||||
* The name that would be provided to Logback to log at this level if
|
* The name that would be provided to Logback to log at this level if
|
||||||
* manually configuring Logback using "logback.xml".
|
* manually configuring Logback using "logback.xml".
|
||||||
|
*
|
||||||
|
* @param pattern
|
||||||
|
* The Logback encoder pattern that should be use for messages logged
|
||||||
|
* at this level.
|
||||||
*/
|
*/
|
||||||
private LogLevel(String canonicalName, String logbackLogLevel) {
|
private LogLevel(String canonicalName, String logbackLogLevel, String pattern) {
|
||||||
this.canonicalName = canonicalName;
|
this.canonicalName = canonicalName;
|
||||||
this.logbackConfig = String.format(LOGBACK_XML_TEMPLATE, logbackLogLevel).getBytes(StandardCharsets.UTF_8);
|
this.logbackConfig = String.format(LOGBACK_XML_TEMPLATE, pattern, logbackLogLevel).getBytes(StandardCharsets.UTF_8);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -131,7 +136,27 @@ public enum LogLevel {
|
|||||||
* "logback.xml" configuration file.
|
* "logback.xml" configuration file.
|
||||||
*/
|
*/
|
||||||
private LogLevel(String logLevel) {
|
private LogLevel(String logLevel) {
|
||||||
this(logLevel, logLevel);
|
this(logLevel, logLevel, "${guac_pattern}");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new LogLevel with the given name and pattern. The provided name
|
||||||
|
* corresponds to both the name used within Guacamole's configuration and
|
||||||
|
* the name used within Logback's configuration, while the pattern
|
||||||
|
* determines how Logback will convert log messages to text.
|
||||||
|
*
|
||||||
|
* @param logLevel
|
||||||
|
* The name that should be used for this log level when configuring
|
||||||
|
* Guacamole to log at this level using the "log-level" property AND
|
||||||
|
* when manually configuring Logback to log at this level using a
|
||||||
|
* "logback.xml" configuration file.
|
||||||
|
*
|
||||||
|
* @param pattern
|
||||||
|
* The Logback encoder pattern that should be use for messages logged
|
||||||
|
* at this level.
|
||||||
|
*/
|
||||||
|
private LogLevel(String logLevel, String pattern) {
|
||||||
|
this(logLevel, logLevel, pattern);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import java.io.IOException;
|
|||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import org.apache.guacamole.GuacamoleException;
|
import org.apache.guacamole.GuacamoleException;
|
||||||
import org.apache.guacamole.environment.Environment;
|
import org.apache.guacamole.environment.Environment;
|
||||||
|
import org.apache.guacamole.net.auth.AuthenticationProvider;
|
||||||
import org.apache.guacamole.properties.EnumGuacamoleProperty;
|
import org.apache.guacamole.properties.EnumGuacamoleProperty;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -50,6 +51,95 @@ public class LogModule extends AbstractModule {
|
|||||||
*/
|
*/
|
||||||
private final Environment environment;
|
private final Environment environment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the MDC key that should be used to store Guacamole-specific
|
||||||
|
* context information. If an extension logs a message, this information
|
||||||
|
* will include the identifier of the relevant authentication provider.
|
||||||
|
*/
|
||||||
|
public static final String MDC_CONTEXT_KEY = "context";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name that should be considered the top-level context when populating
|
||||||
|
* the MDC entry pointed to by {@link #MDC_CONTEXT_KEY}.
|
||||||
|
*/
|
||||||
|
public static final String MDC_CONTEXT_ROOT = "guacamole";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the context information that should be stored in MDC under
|
||||||
|
* {@link #MDC_CONTEXT_KEY} for any message logged by the given
|
||||||
|
* authentication provider.
|
||||||
|
*
|
||||||
|
* @param authProvider
|
||||||
|
* The authentication provider to provide context for.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* The context information that should be stored in MDC for any message
|
||||||
|
* logged by the given authentication provider.
|
||||||
|
*/
|
||||||
|
public static String getMDCContext(AuthenticationProvider authProvider) {
|
||||||
|
return LogModule.MDC_CONTEXT_ROOT + "/" + authProvider.getIdentifier();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Regular expression that matches one or more newlines, regardless of style
|
||||||
|
* (Windows, Linux, etc.), preserving only the first such newline. This
|
||||||
|
* expression is intended to reliably match newlines that may be interpreted
|
||||||
|
* by the shell/viewer, regardless of the platform producing the log
|
||||||
|
* message.
|
||||||
|
*/
|
||||||
|
private static final String NEWLINE_REGEX =
|
||||||
|
|
||||||
|
// Locate the right edge of the first newline sequence, whether
|
||||||
|
// that is CR, LF, CRLF, or LFCR
|
||||||
|
"(?<="
|
||||||
|
|
||||||
|
// Do not match newline sequences that are not the first
|
||||||
|
// newline in the sequence
|
||||||
|
+ "(?<![\\n\\r])"
|
||||||
|
|
||||||
|
// Match either CR, LF, CRLF, or LFCR, ensuring that CRLF
|
||||||
|
// does not get matched as just CR (same for LFCR vs. LF)
|
||||||
|
+ "("
|
||||||
|
+ "\\n(?!\\r)" // LF (with no following CR)
|
||||||
|
+ "|\\r(?!\\n)" // CR (with no following LF)
|
||||||
|
+ "|\\n\\r" // LFCR
|
||||||
|
+ "|\\r\\n" // CRLF
|
||||||
|
+ ")"
|
||||||
|
|
||||||
|
+ ")"
|
||||||
|
|
||||||
|
// Ignore (and strip) and remaining newline characters
|
||||||
|
+ "[\\n\\r]*";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logback encoder pattern fragment that produces a local timestamp (with
|
||||||
|
* timezone information) in roughly ISO 8601 format.
|
||||||
|
*/
|
||||||
|
private static final String LOG_PATTERN_FRAGMENT_TIMESTAMP = "%d{yyyy-MM-dd HH:mm:ss.SSS xxx}";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logback encoder pattern fragment that produces the logged message. This
|
||||||
|
* automatically prepends any newlines within the message with a "+ " prefix
|
||||||
|
* to clearly represent when a message has been split across multiple lines.
|
||||||
|
*/
|
||||||
|
private static final String LOG_PATTERN_FRAGMENT_MESSAGE_BODY = "%replace(%msg){'" + NEWLINE_REGEX + "','+ '}";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logback encoder pattern that should be used for messages when the highest
|
||||||
|
* level of verbosity is not needed.
|
||||||
|
*/
|
||||||
|
private static final String LOG_PATTERN_DEFAULT = LOG_PATTERN_FRAGMENT_TIMESTAMP
|
||||||
|
+ " [%X{" + MDC_CONTEXT_KEY + ":-" + MDC_CONTEXT_ROOT + "}] %level: "
|
||||||
|
+ LOG_PATTERN_FRAGMENT_MESSAGE_BODY + "%n%nopex";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logback encoder pattern that should be used for messages with the highest
|
||||||
|
* level of verbosity.
|
||||||
|
*/
|
||||||
|
private static final String LOG_PATTERN_VERBOSE = LOG_PATTERN_FRAGMENT_TIMESTAMP
|
||||||
|
+ " [%X{" + MDC_CONTEXT_KEY + ":-" + MDC_CONTEXT_ROOT + "}] %logger{48} %level: "
|
||||||
|
+ LOG_PATTERN_FRAGMENT_MESSAGE_BODY + "%n%ex";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Property that specifies the highest level of verbosity that Guacamole
|
* Property that specifies the highest level of verbosity that Guacamole
|
||||||
* should use for the messages in its logs.
|
* should use for the messages in its logs.
|
||||||
@@ -125,6 +215,13 @@ public class LogModule extends AbstractModule {
|
|||||||
LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
|
LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
|
||||||
context.reset();
|
context.reset();
|
||||||
|
|
||||||
|
// Include convenience properties for building off Guacamole's
|
||||||
|
// built-in configuration with a custom logback.xml
|
||||||
|
context.putProperty("guac_timestamp_pattern", LOG_PATTERN_FRAGMENT_TIMESTAMP);
|
||||||
|
context.putProperty("guac_message_pattern", LOG_PATTERN_FRAGMENT_MESSAGE_BODY);
|
||||||
|
context.putProperty("guac_pattern", LOG_PATTERN_DEFAULT);
|
||||||
|
context.putProperty("guac_verbose_pattern", LOG_PATTERN_VERBOSE);
|
||||||
|
|
||||||
// Initialize logback
|
// Initialize logback
|
||||||
JoranConfigurator configurator = new JoranConfigurator();
|
JoranConfigurator configurator = new JoranConfigurator();
|
||||||
configurator.setContext(context);
|
configurator.setContext(context);
|
||||||
|
|||||||
@@ -32,10 +32,12 @@ import javax.ws.rs.core.MediaType;
|
|||||||
import org.apache.guacamole.GuacamoleException;
|
import org.apache.guacamole.GuacamoleException;
|
||||||
import org.apache.guacamole.GuacamoleResourceNotFoundException;
|
import org.apache.guacamole.GuacamoleResourceNotFoundException;
|
||||||
import org.apache.guacamole.GuacamoleSession;
|
import org.apache.guacamole.GuacamoleSession;
|
||||||
|
import org.apache.guacamole.log.LogModule;
|
||||||
import org.apache.guacamole.net.auth.UserContext;
|
import org.apache.guacamole.net.auth.UserContext;
|
||||||
import org.apache.guacamole.rest.auth.AuthenticationService;
|
import org.apache.guacamole.rest.auth.AuthenticationService;
|
||||||
import org.apache.guacamole.rest.tunnel.TunnelCollectionResource;
|
import org.apache.guacamole.rest.tunnel.TunnelCollectionResource;
|
||||||
import org.apache.guacamole.rest.tunnel.TunnelCollectionResourceFactory;
|
import org.apache.guacamole.rest.tunnel.TunnelCollectionResourceFactory;
|
||||||
|
import org.slf4j.MDC;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A REST resource which exposes all data associated with a Guacamole user's
|
* A REST resource which exposes all data associated with a Guacamole user's
|
||||||
@@ -116,8 +118,16 @@ public class SessionResource {
|
|||||||
// Pull UserContext defined by the given auth provider identifier
|
// Pull UserContext defined by the given auth provider identifier
|
||||||
UserContext userContext = session.getUserContext(authProviderIdentifier);
|
UserContext userContext = session.getUserContext(authProviderIdentifier);
|
||||||
|
|
||||||
// Return a resource exposing the retrieved UserContext
|
MDC.put(LogModule.MDC_CONTEXT_KEY, LogModule.getMDCContext(userContext.getAuthenticationProvider()));
|
||||||
return userContextResourceFactory.create(session.getAuthenticatedUser(), userContext);
|
try {
|
||||||
|
|
||||||
|
// Return a resource exposing the retrieved UserContext
|
||||||
|
return userContextResourceFactory.create(session.getAuthenticatedUser(), userContext);
|
||||||
|
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
MDC.put(LogModule.MDC_CONTEXT_KEY, LogModule.MDC_CONTEXT_ROOT);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,13 +155,21 @@ public class SessionResource {
|
|||||||
// Pull UserContext defined by the given auth provider identifier
|
// Pull UserContext defined by the given auth provider identifier
|
||||||
UserContext userContext = session.getUserContext(authProviderIdentifier);
|
UserContext userContext = session.getUserContext(authProviderIdentifier);
|
||||||
|
|
||||||
// Pull resource from user context
|
MDC.put(LogModule.MDC_CONTEXT_KEY, LogModule.getMDCContext(userContext.getAuthenticationProvider()));
|
||||||
Object resource = userContext.getResource();
|
try {
|
||||||
if (resource != null)
|
|
||||||
return resource;
|
|
||||||
|
|
||||||
// UserContext-specific resource could not be found
|
// Pull resource from user context
|
||||||
throw new GuacamoleResourceNotFoundException("No such resource.");
|
Object resource = userContext.getResource();
|
||||||
|
if (resource != null)
|
||||||
|
return resource;
|
||||||
|
|
||||||
|
// UserContext-specific resource could not be found
|
||||||
|
throw new GuacamoleResourceNotFoundException("No such resource.");
|
||||||
|
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
MDC.put(LogModule.MDC_CONTEXT_KEY, LogModule.MDC_CONTEXT_ROOT);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
org.apache.guacamole.log.GuacamoleLogbackServiceProvider
|
||||||
Reference in new issue
Block a user