Skip to content

Commit aee8479

Browse files
authored
Merge pull request #1373 from microsoft/trask/logging-improvements
Logging improvements
2 parents 77d5902 + 28365c7 commit aee8479

File tree

55 files changed

+256
-151
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+256
-151
lines changed

agent/agent-bootstrap/src/main/java/com/microsoft/applicationinsights/agent/bootstrap/MainEntryPoint.java

Lines changed: 71 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,21 @@
2121
package com.microsoft.applicationinsights.agent.bootstrap;
2222

2323
import java.io.File;
24+
import java.lang.instrument.ClassFileTransformer;
25+
import java.lang.instrument.IllegalClassFormatException;
2426
import java.lang.instrument.Instrumentation;
2527
import java.net.URL;
2628
import java.nio.file.Path;
29+
import java.security.ProtectionDomain;
2730

31+
import ch.qos.logback.classic.Level;
2832
import com.microsoft.applicationinsights.agent.bootstrap.configuration.Configuration;
29-
import com.microsoft.applicationinsights.agent.bootstrap.configuration.ConfigurationBuilder;
3033
import com.microsoft.applicationinsights.agent.bootstrap.configuration.Configuration.SelfDiagnostics;
34+
import com.microsoft.applicationinsights.agent.bootstrap.configuration.ConfigurationBuilder;
3135
import com.microsoft.applicationinsights.agent.bootstrap.diagnostics.DiagnosticsHelper;
3236
import com.microsoft.applicationinsights.agent.bootstrap.diagnostics.status.StatusFile;
33-
import io.opentelemetry.javaagent.bootstrap.ConfigureLogging;
3437
import io.opentelemetry.javaagent.bootstrap.AgentInitializer;
38+
import io.opentelemetry.javaagent.bootstrap.ConfigureLogging;
3539
import org.slf4j.Logger;
3640
import org.slf4j.LoggerFactory;
3741
import org.slf4j.MDC;
@@ -62,41 +66,45 @@ public static void start(Instrumentation instrumentation, URL bootstrapURL) {
6266
boolean success = false;
6367
Logger startupLogger = null;
6468
try {
65-
File agentJarFile = new File(bootstrapURL.toURI());
66-
DiagnosticsHelper.setAgentJarFile(agentJarFile);
69+
Path agentPath = new File(bootstrapURL.toURI()).toPath();
70+
DiagnosticsHelper.setAgentJarFile(agentPath);
6771
// configuration is only read this early in order to extract logging configuration
68-
configuration = ConfigurationBuilder.create(agentJarFile.toPath());
72+
configuration = ConfigurationBuilder.create(agentPath);
6973
configPath = configuration.configPath;
7074
lastModifiedTime = configuration.lastModifiedTime;
71-
startupLogger = configureLogging(configuration.preview.selfDiagnostics);
75+
startupLogger = configureLogging(configuration.selfDiagnostics, agentPath);
7276
ConfigurationBuilder.logConfigurationMessages();
7377
MDC.put(DiagnosticsHelper.MDC_PROP_OPERATION, "Startup");
7478
AgentInitializer.start(instrumentation, bootstrapURL, false);
79+
startupLogger.info("ApplicationInsights Java Agent started successfully");
7580
success = true;
7681
LoggerFactory.getLogger(DiagnosticsHelper.DIAGNOSTICS_LOGGER_NAME)
7782
.info("Application Insights Codeless Agent Attach Successful");
7883
} catch (ThreadDeath td) {
7984
throw td;
8085
} catch (Throwable t) {
8186
if (startupLogger != null) {
82-
startupLogger.error("Agent failed to start.", t);
87+
startupLogger.error("ApplicationInsights Java Agent failed to start", t);
8388
} else {
8489
t.printStackTrace();
8590
}
8691
} finally {
8792
try {
88-
StatusFile.putValueAndWrite("AgentInitializedSuccessfully", success);
93+
StatusFile.putValueAndWrite("AgentInitializedSuccessfully", success, startupLogger != null);
8994
} catch (Exception e) {
90-
startupLogger.error("Error writing status.json", e); // lgtm[java/dereferenced-value-may-be-null]
95+
if (startupLogger != null) {
96+
startupLogger.error("Error writing status.json", e);
97+
} else {
98+
e.printStackTrace();
99+
}
91100
}
92101
MDC.clear();
93102
}
94103
}
95104

96-
private static Logger configureLogging(SelfDiagnostics selfDiagnostics) {
105+
private static Logger configureLogging(SelfDiagnostics selfDiagnostics, Path agentPath) {
97106
String logbackXml;
98107
String destination = selfDiagnostics.destination;
99-
boolean logUnknownDestination = false;
100108
if (DiagnosticsHelper.isAppServiceCodeless()) {
101109
// User-accessible IPA log file. Enabled by default.
102110
if ("false".equalsIgnoreCase(System.getenv(DiagnosticsHelper.IPA_LOG_FILE_ENABLED_ENV_VAR))) {
@@ -114,58 +122,80 @@ private static Logger configureLogging(SelfDiagnostics selfDiagnostics) {
114122
System.setProperty("ai.config.appender.etw.location", "");
115123
}
116124
logbackXml = "applicationinsights.appsvc.logback.xml";
117-
} else if (destination == null || destination.equalsIgnoreCase("console")) {
118-
logbackXml = "applicationinsights.console.logback.xml";
125+
} else if (destination == null || destination.equalsIgnoreCase("file+console")) {
126+
logbackXml = "applicationinsights.file-and-console.logback.xml";
119127
} else if (destination.equalsIgnoreCase("file")) {
120128
logbackXml = "applicationinsights.file.logback.xml";
121-
} else {
122-
logUnknownDestination = true;
129+
} else if (destination.equalsIgnoreCase("console")) {
123130
logbackXml = "applicationinsights.console.logback.xml";
131+
} else {
132+
throw new IllegalStateException("Unknown self-diagnostics destination: " + destination);
124133
}
125134
ClassLoader cl = ConfigureLogging.class.getClassLoader();
126135
if (cl == null) {
127136
cl = ClassLoader.getSystemClassLoader();
128137
}
129138
final URL configurationFile = cl.getResource(logbackXml);
130-
System.setProperty("applicationinsights.logback.configurationFile", configurationFile.toString());
131139

132-
String logbackDirectory = selfDiagnostics.directory;
133-
String logbackLevel = selfDiagnostics.level;
134-
int logbackMaxFileSizeMB = selfDiagnostics.maxSizeMB;
140+
Level level = getLevel(selfDiagnostics.level);
135141

136-
if (logbackDirectory == null) {
137-
logbackDirectory = System.getProperty("java.io.tmpdir");
138-
}
139-
if (logbackDirectory == null) {
140-
// this should never get to here, but just in case, otherwise setProperty() will fail below
141-
logbackDirectory = ".";
142+
Path logFilePath = agentPath.resolveSibling(selfDiagnostics.file.path);
143+
Path logFileNamePath = logFilePath.getFileName();
144+
if (logFileNamePath == null) {
145+
throw new IllegalStateException("Unexpected empty self-diagnostics file path");
142146
}
143-
System.setProperty("applicationinsights.logback.directory", logbackDirectory);
144-
System.setProperty("applicationinsights.logback.level", logbackLevel);
145-
System.setProperty("applicationinsights.logback.maxFileSize", logbackMaxFileSizeMB + "MB");
146-
System.setProperty("applicationinsights.logback.totalSizeCap", logbackMaxFileSizeMB * 2 + "MB");
147-
if (isDebugOrLower(logbackLevel)) {
148-
// never want to log apache http at trace or debug, it's just way to verbose
149-
System.setProperty("applicationinsights.logback.level.org.apache.http", "info");
147+
String logFileName = logFileNamePath.toString();
148+
String rollingFileName;
149+
int index = logFileName.lastIndexOf('.');
150+
if (index != -1) {
151+
rollingFileName = logFileName.substring(0, index) + ".%i" + logFileName.substring(index);
150152
} else {
151-
System.setProperty("applicationinsights.logback.level.org.apache.http", logbackLevel);
153+
rollingFileName = logFileName + ".%i";
152154
}
155+
Path rollingFilePath = logFilePath.resolveSibling(rollingFileName);
156+
157+
// never want to log apache http at trace or debug, it's just way to verbose
158+
Level atLeastInfoLevel = getMaxLevel(level, Level.INFO);
159+
160+
Level otherLibsLevel = level == Level.INFO ? Level.WARN : level;
161+
153162
try {
154-
Logger logger = LoggerFactory.getLogger("com.microsoft.applicationinsights.agent");
155-
if (logUnknownDestination) {
156-
logger.error("Unknown self-diagnostics destination: {}", destination);
157-
}
158-
return logger;
163+
System.setProperty("applicationinsights.logback.configurationFile", configurationFile.toString());
164+
165+
System.setProperty("applicationinsights.logback.file.path", logFilePath.toString());
166+
System.setProperty("applicationinsights.logback.file.rollingPath", rollingFilePath.toString());
167+
System.setProperty("applicationinsights.logback.file.maxSize", selfDiagnostics.file.maxSizeMb + "MB");
168+
System.setProperty("applicationinsights.logback.file.maxIndex", Integer.toString(selfDiagnostics.file.maxHistory));
169+
170+
System.setProperty("applicationinsights.logback.level.other", otherLibsLevel.toString());
171+
System.setProperty("applicationinsights.logback.level", level.levelStr);
172+
173+
System.setProperty("applicationinsights.logback.level.atLeastInfo", atLeastInfoLevel.levelStr);
174+
175+
return LoggerFactory.getLogger("com.microsoft.applicationinsights.agent");
159176
} finally {
160177
System.clearProperty("applicationinsights.logback.configurationFile");
161-
System.clearProperty("applicationinsights.logback.directory");
178+
System.clearProperty("applicationinsights.logback.file.path");
179+
System.clearProperty("applicationinsights.logback.file.rollingPath");
180+
System.clearProperty("applicationinsights.logback.file.maxSize");
181+
System.clearProperty("applicationinsights.logback.file.maxIndex");
162182
System.clearProperty("applicationinsights.logback.level");
163-
System.clearProperty("applicationinsights.logback.maxFileSize");
164-
System.clearProperty("applicationinsights.logback.totalSizeCap");
165183
System.clearProperty("applicationinsights.logback.level.org.apache.http");
166184
}
167185
}
168186

187+
private static Level getMaxLevel(Level level1, Level level2) {
188+
return level1.isGreaterOrEqual(level2) ? level1 : level2;
189+
}
190+
191+
private static Level getLevel(String levelStr) {
192+
Level level = Level.valueOf(levelStr);
193+
if (!level.levelStr.equalsIgnoreCase(levelStr)) {
194+
throw new IllegalStateException("Unexpected self-diagnostic level: " + levelStr);
195+
}
196+
return level;
197+
}
198+
169199
private static boolean isDebugOrLower(String level) {
170200
return level.equalsIgnoreCase("all") || level.equalsIgnoreCase("trace") || level.equalsIgnoreCase("debug");
171201
}

agent/agent-bootstrap/src/main/java/com/microsoft/applicationinsights/agent/bootstrap/configuration/Configuration.java

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,12 @@ public class Configuration {
4343
public Map<String, Map<String, Object>> instrumentation = new HashMap<String, Map<String, Object>>();
4444
public Heartbeat heartbeat = new Heartbeat();
4545
public Proxy proxy = new Proxy();
46+
public SelfDiagnostics selfDiagnostics = new SelfDiagnostics();
4647
public PreviewConfiguration preview = new PreviewConfiguration();
4748

49+
// this is just here to detect if using old format in order to give a helpful error message
50+
public Map<String, Object> instrumentationSettings;
51+
4852
public enum ProcessorMatchType {
4953
//Moshi JSON builder donot allow case insensitive mapping
5054
strict, regexp
@@ -91,18 +95,23 @@ public static class Proxy {
9195

9296
public static class PreviewConfiguration {
9397

94-
public SelfDiagnostics selfDiagnostics = new SelfDiagnostics();
9598
public boolean developerMode;
9699
public List<ProcessorConfig> processors = new ArrayList<>();
97100
public boolean openTelemetryApiSupport;
98101
}
99102

100103
public static class SelfDiagnostics {
101104

102-
public String destination;
103-
public String directory;
104-
public String level = "error";
105-
public int maxSizeMB = 10;
105+
public String level = "info";
106+
public String destination = "file+console";
107+
public DestinationFile file = new DestinationFile();
108+
}
109+
110+
public static class DestinationFile {
111+
112+
public String path = "applicationinsights.log"; // relative to the directory where agent jar is located
113+
public int maxSizeMb = 5;
114+
public int maxHistory = 1;
106115
}
107116

108117
public static class ProcessorConfig {

agent/agent-bootstrap/src/main/java/com/microsoft/applicationinsights/agent/bootstrap/configuration/ConfigurationBuilder.java

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ private static void loadLogCaptureEnvVar(Configuration config) {
8484
private static void loadJmxMetrics(Configuration config) throws IOException {
8585
String jmxMetricsEnvVarJson = overlayWithEnvVar(APPLICATIONINSIGHTS_JMX_METRICS, (String)null);
8686

87-
// JmxMetrics env variable has higher precedence over jmxMetrics config from ApplicationInsights.json
87+
// JmxMetrics env variable has higher precedence over jmxMetrics config from applicationinsights.json
8888
if (jmxMetricsEnvVarJson != null && !jmxMetricsEnvVarJson.isEmpty()) {
8989
Moshi moshi = new Moshi.Builder().build();
9090
Type listOfJmxMetrics = Types.newParameterizedType(List.class, JmxMetric.class);
@@ -124,25 +124,28 @@ private static Configuration loadConfigurationFile(Path agentJarPath) throws IOE
124124
return new Configuration();
125125
}
126126

127-
Path configPath;
128-
boolean warnIfMissing;
129127
String configPathStr = getEnvVarOrProperty(APPLICATIONINSIGHTS_CONFIGURATION_FILE, "applicationinsights.configuration.file");
130-
if (configPathStr == null) {
131-
configPath = agentJarPath.resolveSibling("ApplicationInsights.json");
132-
warnIfMissing = false;
133-
} else {
134-
configPath = agentJarPath.resolveSibling(configPathStr);
135-
warnIfMissing = true;
128+
if (configPathStr != null) {
129+
Path configPath = agentJarPath.resolveSibling(configPathStr);
130+
if (Files.exists(configPath)) {
131+
return loadJsonConfigFile(configPath);
132+
} else {
133+
// fail fast any time configuration is invalid
134+
throw new IllegalStateException("could not find requested configuration file: " + configPathStr);
135+
}
136136
}
137137

138+
Path configPath = agentJarPath.resolveSibling("applicationinsights.json");
138139
if (Files.exists(configPath)) {
139140
return loadJsonConfigFile(configPath);
140-
} else {
141-
if (warnIfMissing) {
142-
configurationMessages.add(new ConfigurationMessage("could not find configuration file: {}", configPathStr));
143-
}
144-
return new Configuration();
145141
}
142+
143+
if (Files.exists(agentJarPath.resolveSibling("ApplicationInsights.json"))) {
144+
throw new IllegalStateException("found ApplicationInsights.json, but it should be lowercase: applicationinsights.json");
145+
}
146+
147+
// json configuration file is not required, ok to configure via env var alone
148+
return new Configuration();
146149
}
147150

148151
public static void logConfigurationMessages() {
@@ -293,6 +296,11 @@ public static Configuration loadJsonConfigFile(Path configPath) throws IOExcepti
293296
buffer.readFrom(in);
294297
try {
295298
Configuration configuration = jsonAdapter.fromJson(buffer);
299+
if (configuration.instrumentationSettings != null) {
300+
throw new IllegalStateException("It looks like you are using an old applicationinsights.json file" +
301+
" which still has \"instrumentationSettings\", please see the docs for the new format:" +
302+
" https://docs.microsoft.com/en-us/azure/azure-monitor/app/java-standalone-config");
303+
}
296304
configuration.configPath = configPath;
297305
configuration.lastModifiedTime = lastModifiedTime;
298306
return configuration;

agent/agent-bootstrap/src/main/java/com/microsoft/applicationinsights/agent/bootstrap/diagnostics/DiagnosticsHelper.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,7 @@ private DiagnosticsHelper() { }
4545
isWindows = osName != null && osName.startsWith("Windows");
4646
}
4747

48-
public static void setAgentJarFile(File agentJarFile) {
49-
Path agentPath = agentJarFile.toPath();
48+
public static void setAgentJarFile(Path agentPath) {
5049
if (Files.exists(agentPath.resolveSibling("appsvc.codeless"))) {
5150
appServiceCodeless = true;
5251
} else if (Files.exists(agentPath.resolveSibling("aks.codeless"))) {

agent/agent-bootstrap/src/main/java/com/microsoft/applicationinsights/agent/bootstrap/diagnostics/status/StatusFile.java

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,6 @@
3030

3131
public class StatusFile {
3232

33-
private static final Logger logger = LoggerFactory.getLogger(StatusFile.class);
34-
3533
private static final List<DiagnosticsValueFinder> VALUE_FINDERS = new ArrayList<>();
3634

3735
// visible for testing
@@ -131,11 +129,15 @@ static boolean shouldWrite() {
131129
}
132130

133131
public static <T> void putValueAndWrite(String key, T value) {
132+
putValueAndWrite(key, value, true);
133+
}
134+
135+
public static <T> void putValueAndWrite(String key, T value, boolean loggingInitialized) {
134136
if (!shouldWrite()) {
135137
return;
136138
}
137139
CONSTANT_VALUES.put(key, value);
138-
write();
140+
write(loggingInitialized);
139141
}
140142

141143
public static <T> void putValue(String key, T value) {
@@ -146,6 +148,10 @@ public static <T> void putValue(String key, T value) {
146148
}
147149

148150
public static void write() {
151+
write(false);
152+
}
153+
154+
private static void write(boolean loggingInitialized) {
149155
if (!shouldWrite()) {
150156
return;
151157
}
@@ -161,15 +167,26 @@ public void run() {
161167
synchronized (lock) {
162168
final File file = new File(directory, fileName);
163169
boolean dirsWereCreated = file.getParentFile().mkdirs();
170+
171+
Logger logger = loggingInitialized ? LoggerFactory.getLogger(StatusFile.class) : null;
172+
164173
if (dirsWereCreated || file.getParentFile().exists()) {
165174
BufferedSink b = null;
166175
try {
167176
b = getBuffer(file);
168177
new Moshi.Builder().build().adapter(Map.class).indent(" ").nullSafe().toJson(b, map);
169178
b.flush();
170-
logger.info("Wrote status to file: {}", file.getAbsolutePath());
179+
if (logger != null) {
180+
logger.info("Wrote status to file: {}", file.getAbsolutePath());
181+
} else {
182+
System.out.println("Wrote status to file: " + file.getAbsolutePath());
183+
}
171184
} catch (Exception e) {
172-
logger.error("Error writing {}", file.getAbsolutePath(), e);
185+
if (logger != null) {
186+
logger.error("Error writing {}", file.getAbsolutePath(), e);
187+
} else {
188+
e.printStackTrace();
189+
}
173190
if (b != null) {
174191
try {
175192
b.close();
@@ -179,8 +196,13 @@ public void run() {
179196
}
180197
}
181198
} else {
182-
logger.error("Parent directories for status file could not be created: {}",
183-
file.getAbsolutePath());
199+
if (logger != null) {
200+
logger.error("Parent directories for status file could not be created: {}",
201+
file.getAbsolutePath());
202+
} else {
203+
System.err.println("Parent directories for status file could not be created: "
204+
+ file.getAbsolutePath());
205+
}
184206
}
185207
}
186208
}

0 commit comments

Comments
 (0)