Skip to content

Commit f3600d2

Browse files
committed
HADOOP-19272. S3A: AWS SDK 2.25.53 warnings logged by transfer manager (#7048)
Disables all logging below error in the AWS SDK Transfer Manager. This is done in ClientManagerImpl construction so is automatically done during S3A FS initialization. ITests verify that * It is possible to restore the warning log. This verifies the validity of the test suite, and will identify when an SDK update fixes this regression. * Constructing an S3A FS instance will disable the logging. The log manipulation code is lifted from Cloudstore, where it was used to dynamically enable logging. It uses reflection to load the Log4J binding; all uses of the API catch and swallow exceptions. This is needed to avoid failures when running against different log backends This is an emergency fix -we could come up with a better design for the reflection based code using the new DynMethods classes. But this is based on working code, which is always good. Contributed by Steve Loughran
1 parent 42bd833 commit f3600d2

File tree

8 files changed

+707
-0
lines changed

8 files changed

+707
-0
lines changed
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
package org.apache.hadoop.fs.s3a.impl;
20+
21+
import org.apache.hadoop.classification.VisibleForTesting;
22+
import org.apache.hadoop.fs.s3a.impl.logging.LogControl;
23+
import org.apache.hadoop.fs.s3a.impl.logging.LogControllerFactory;
24+
25+
/**
26+
* This class exists to support workarounds for parts of the AWS SDK
27+
* which have caused problems.
28+
*/
29+
public final class AwsSdkWorkarounds {
30+
31+
/**
32+
* Transfer manager log name. See HADOOP-19272.
33+
* {@value}.
34+
*/
35+
public static final String TRANSFER_MANAGER =
36+
"software.amazon.awssdk.transfer.s3.S3TransferManager";
37+
38+
private AwsSdkWorkarounds() {
39+
}
40+
41+
/**
42+
* Prepare logging before creating AWS clients.
43+
* @return true if the log tuning operation took place.
44+
*/
45+
public static boolean prepareLogging() {
46+
return LogControllerFactory.createController().
47+
setLogLevel(TRANSFER_MANAGER, LogControl.LogLevel.ERROR);
48+
}
49+
50+
/**
51+
* Restore all noisy logs to INFO.
52+
* @return true if the restoration operation took place.
53+
*/
54+
@VisibleForTesting
55+
static boolean restoreNoisyLogging() {
56+
return LogControllerFactory.createController().
57+
setLogLevel(TRANSFER_MANAGER, LogControl.LogLevel.INFO);
58+
}
59+
}

hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/impl/ClientManagerImpl.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,10 @@ public class ClientManagerImpl implements ClientManager {
9090

9191
/**
9292
* Constructor.
93+
* <p>
9394
* This does not create any clients.
95+
* <p>
96+
* It does disable noisy logging from the S3 Transfer Manager.
9497
* @param clientFactory client factory to invoke
9598
* @param clientCreationParameters creation parameters.
9699
* @param durationTrackerFactory duration tracker.
@@ -105,6 +108,9 @@ public ClientManagerImpl(
105108
this.s3Client = new LazyAutoCloseableReference<>(createS3Client());
106109
this.s3AsyncClient = new LazyAutoCloseableReference<>(createAyncClient());
107110
this.transferManager = new LazyAutoCloseableReference<>(createTransferManager());
111+
112+
// fix up SDK logging.
113+
AwsSdkWorkarounds.prepareLogging();
108114
}
109115

110116
/**
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
package org.apache.hadoop.fs.s3a.impl.logging;
20+
21+
import org.apache.log4j.Level;
22+
import org.apache.log4j.Logger;
23+
24+
/**
25+
* Something to control logging levels in Log4j.
26+
* <p>
27+
* Package private to avoid any direct instantiation.
28+
* <p>
29+
* Important: this must never be instantiated exception through
30+
* reflection code which can catch and swallow exceptions related
31+
* to not finding Log4J on the classpath.
32+
* The Hadoop libraries can and are used with other logging
33+
* back ends and we MUST NOT break that.
34+
*/
35+
class Log4JController extends LogControl {
36+
37+
/**
38+
* Set the log4J level, ignoring all exceptions raised.
39+
* {@inheritDoc}
40+
*/
41+
@Override
42+
protected boolean setLevel(final String logName, final LogLevel level) {
43+
try {
44+
Logger logger = Logger.getLogger(logName);
45+
logger.setLevel(Level.toLevel(level.getLog4Jname()));
46+
return true;
47+
} catch (Exception ignored) {
48+
// ignored.
49+
return false;
50+
}
51+
}
52+
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
package org.apache.hadoop.fs.s3a.impl.logging;
20+
21+
/**
22+
* class to assist reflection-based control of logger back ends.
23+
* <p>
24+
* An instance of LogControl is able to control the log levels of
25+
* loggers for log libraries such as Log4j, yet can be created in
26+
* code designed to support multiple back end loggers behind
27+
* SLF4J.
28+
*/
29+
public abstract class LogControl {
30+
31+
/**
32+
* Enumeration of log levels.
33+
* <p>
34+
* The list is in descending order.
35+
*/
36+
public enum LogLevel {
37+
ALL("ALL"),
38+
FATAL("FATAL"),
39+
ERROR("ERROR"),
40+
WARN("WARN"),
41+
INFO("INFO"),
42+
DEBUG("DEBUG"),
43+
TRACE("TRACE"),
44+
OFF("OFF");
45+
46+
/**
47+
* Level name as used in Log4J.
48+
*/
49+
private final String log4Jname;
50+
51+
LogLevel(final String log4Jname) {
52+
this.log4Jname = log4Jname;
53+
}
54+
55+
/**
56+
* Get the log4j name of this level.
57+
* @return the log name for use in configuring Log4J.
58+
*/
59+
public String getLog4Jname() {
60+
return log4Jname;
61+
}
62+
}
63+
64+
/**
65+
* Sets a log level for a class/package.
66+
* @param log log to set
67+
* @param level level to set
68+
* @return true if the log was set
69+
*/
70+
public final boolean setLogLevel(String log, LogLevel level) {
71+
try {
72+
return setLevel(log, level);
73+
} catch (Exception ignored) {
74+
// ignored.
75+
return false;
76+
}
77+
78+
}
79+
80+
81+
/**
82+
* Sets a log level for a class/package.
83+
* Exceptions may be raised; they will be caught in
84+
* {@link #setLogLevel(String, LogLevel)} and ignored.
85+
* @param log log to set
86+
* @param level level to set
87+
* @return true if the log was set
88+
* @throws Exception any problem loading/updating the log
89+
*/
90+
protected abstract boolean setLevel(String log, LogLevel level) throws Exception;
91+
92+
}
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
package org.apache.hadoop.fs.s3a.impl.logging;
20+
21+
import org.slf4j.Logger;
22+
import org.slf4j.LoggerFactory;
23+
24+
import org.apache.hadoop.fs.store.LogExactlyOnce;
25+
26+
/**
27+
* Factory for creating controllers.
28+
* <p>
29+
* It currently only supports Log4J as a back end.
30+
*/
31+
public final class LogControllerFactory {
32+
33+
private static final Logger LOG = LoggerFactory.getLogger(LogControllerFactory.class);
34+
35+
/**
36+
* Log once: when there are logging issues, logging lots just
37+
* makes it worse.
38+
*/
39+
private static final LogExactlyOnce LOG_ONCE = new LogExactlyOnce(LOG);
40+
41+
/**
42+
* Class name of log controller implementation to be loaded
43+
* through reflection.
44+
* {@value}.
45+
*/
46+
private static final String LOG4J_CONTROLLER =
47+
"org.apache.hadoop.fs.s3a.impl.logging.Log4JController";
48+
49+
private LogControllerFactory() {
50+
}
51+
52+
/**
53+
* Create a controller. Failure to load is logged at debug
54+
* and null is returned.
55+
* @param classname name of controller to load and create.
56+
* @return the instantiated controller or null if it failed to load
57+
*/
58+
public static LogControl createController(String classname) {
59+
try {
60+
Class<?> clazz = Class.forName(classname);
61+
return (LogControl) clazz.newInstance();
62+
} catch (Exception e) {
63+
LOG_ONCE.debug("Failed to create controller {}: {}", classname, e, e);
64+
return null;
65+
}
66+
}
67+
68+
/**
69+
* Create a Log4J controller.
70+
* @return the instantiated controller or null if the class can't be instantiated.
71+
*/
72+
public static LogControl createLog4JController() {
73+
return createController(LOG4J_CONTROLLER);
74+
}
75+
76+
/**
77+
* Create a controller, Log4j or falling back to a stub implementation.
78+
* @return the instantiated controller or empty() if the class can't be instantiated.
79+
*/
80+
public static LogControl createController() {
81+
final LogControl controller = createLog4JController();
82+
return controller != null
83+
? controller
84+
: new StubLogControl();
85+
}
86+
87+
/**
88+
* Stub controller which always reports false.
89+
*/
90+
private static final class StubLogControl extends LogControl {
91+
92+
@Override
93+
protected boolean setLevel(final String log, final LogLevel level) {
94+
return false;
95+
96+
}
97+
}
98+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
/**
20+
* This package contains reflection-based code to manipulate logging
21+
* levels in external libraries.
22+
*/
23+
@InterfaceAudience.Private
24+
package org.apache.hadoop.fs.s3a.impl.logging;
25+
26+
import org.apache.hadoop.classification.InterfaceAudience;

0 commit comments

Comments
 (0)