Skip to content

Commit efebd84

Browse files
committed
add build-time android.util.Log call-site substitutions. maybe fixes open-telemetry#142
1 parent 642606c commit efebd84

File tree

12 files changed

+632
-0
lines changed

12 files changed

+632
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
plugins {
2+
id("otel.android-library-conventions")
3+
id("otel.publish-conventions")
4+
}
5+
6+
description = "OpenTelemetry Android Logging instrumentation"
7+
8+
android {
9+
namespace = "io.opentelemetry.android.instrumentation.log"
10+
}
11+
12+
dependencies {
13+
api(platform(libs.opentelemetry.platform.alpha))
14+
api(libs.opentelemetry.api)
15+
16+
implementation(libs.androidx.core)
17+
implementation(libs.byteBuddy)
18+
implementation(project(":instrumentation:android-log:library"))
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.agent.log;
7+
8+
import static net.bytebuddy.matcher.ElementMatchers.is;
9+
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
10+
11+
import android.util.Log;
12+
13+
import net.bytebuddy.asm.MemberSubstitution;
14+
import net.bytebuddy.build.AndroidDescriptor;
15+
import net.bytebuddy.build.Plugin;
16+
import net.bytebuddy.description.type.TypeDescription;
17+
import net.bytebuddy.dynamic.ClassFileLocator;
18+
import net.bytebuddy.dynamic.DynamicType;
19+
20+
import java.io.IOException;
21+
22+
import io.opentelemetry.instrumentation.library.log.AndroidLogSubstitutions;
23+
24+
public class AndroidLogPlugin implements Plugin {
25+
private final AndroidDescriptor androidDescriptor;
26+
27+
public AndroidLogPlugin(AndroidDescriptor androidDescriptor) {
28+
this.androidDescriptor = androidDescriptor;
29+
}
30+
31+
@Override
32+
public DynamicType.Builder<?> apply(
33+
DynamicType.Builder<?> builder,
34+
TypeDescription typeDescription,
35+
ClassFileLocator classFileLocator) {
36+
try {
37+
return builder.visit(MemberSubstitution.relaxed()
38+
.method(is(Log.class.getDeclaredMethod("v", String.class, String.class)))
39+
.replaceWith(
40+
AndroidLogSubstitutions.class.getDeclaredMethod(
41+
"substitutionForVerbose", String.class, String.class))
42+
.method(is(Log.class.getDeclaredMethod("v", String.class, String.class, Throwable.class)))
43+
.replaceWith(
44+
AndroidLogSubstitutions.class.getDeclaredMethod(
45+
"substitutionForVerbose2", String.class, String.class, Throwable.class))
46+
.method(is(Log.class.getDeclaredMethod("d", String.class, String.class)))
47+
.replaceWith(
48+
AndroidLogSubstitutions.class.getDeclaredMethod(
49+
"substitutionForDebug", String.class, String.class))
50+
.method(is(Log.class.getDeclaredMethod("d", String.class, String.class, Throwable.class)))
51+
.replaceWith(
52+
AndroidLogSubstitutions.class.getDeclaredMethod(
53+
"substitutionForDebug2", String.class, String.class, Throwable.class))
54+
.method(is(Log.class.getDeclaredMethod("i", String.class, String.class)))
55+
.replaceWith(
56+
AndroidLogSubstitutions.class.getDeclaredMethod(
57+
"substitutionForInfo", String.class, String.class))
58+
.method(is(Log.class.getDeclaredMethod("i", String.class, String.class, Throwable.class)))
59+
.replaceWith(
60+
AndroidLogSubstitutions.class.getDeclaredMethod(
61+
"substitutionForInfo2", String.class, String.class, Throwable.class))
62+
.method(is(Log.class.getDeclaredMethod("w", String.class, String.class)))
63+
.replaceWith(
64+
AndroidLogSubstitutions.class.getDeclaredMethod(
65+
"substitutionForWarn", String.class, String.class))
66+
.method(is(Log.class.getDeclaredMethod("w", String.class, Throwable.class)))
67+
.replaceWith(
68+
AndroidLogSubstitutions.class.getDeclaredMethod(
69+
"substitutionForWarn2", String.class, Throwable.class))
70+
.method(is(Log.class.getDeclaredMethod("w", String.class, String.class, Throwable.class)))
71+
.replaceWith(
72+
AndroidLogSubstitutions.class.getDeclaredMethod(
73+
"substitutionForWarn3", String.class, String.class, Throwable.class))
74+
.method(is(Log.class.getDeclaredMethod("e", String.class, String.class)))
75+
.replaceWith(
76+
AndroidLogSubstitutions.class.getDeclaredMethod(
77+
"substitutionForError", String.class, String.class))
78+
.method(is(Log.class.getDeclaredMethod("e", String.class, String.class, Throwable.class)))
79+
.replaceWith(
80+
AndroidLogSubstitutions.class.getDeclaredMethod(
81+
"substitutionForError2", String.class, String.class, Throwable.class))
82+
.method(is(Log.class.getDeclaredMethod("wtf", String.class, String.class)))
83+
.replaceWith(
84+
AndroidLogSubstitutions.class.getDeclaredMethod(
85+
"substitutionForWtf", String.class, String.class))
86+
.method(is(Log.class.getDeclaredMethod("wtf", String.class, Throwable.class)))
87+
.replaceWith(
88+
AndroidLogSubstitutions.class.getDeclaredMethod(
89+
"substitutionForWtf2", String.class, Throwable.class))
90+
.method(is(Log.class.getDeclaredMethod("wtf", String.class, String.class, Throwable.class)))
91+
.replaceWith(
92+
AndroidLogSubstitutions.class.getDeclaredMethod(
93+
"substitutionForWtf3", String.class, String.class, Throwable.class))
94+
.on(isMethod()));
95+
96+
} catch (NoSuchMethodException e) {
97+
throw new RuntimeException(e);
98+
}
99+
}
100+
101+
@Override
102+
public void close() throws IOException {
103+
// No operation.
104+
}
105+
106+
@Override
107+
public boolean matches(TypeDescription target) {
108+
return androidDescriptor.getTypeScope(target) == AndroidDescriptor.TypeScope.LOCAL;
109+
}
110+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
io.opentelemetry.instrumentation.agent.log.AndroidLogPlugin
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
plugins {
2+
id("otel.android-library-conventions")
3+
id("otel.publish-conventions")
4+
}
5+
6+
description = "OpenTelemetry Android Log library instrumentation for Android"
7+
8+
android {
9+
namespace = "io.opentelemetry.android.log.library"
10+
}
11+
12+
dependencies {
13+
api(project(":instrumentation:android-instrumentation"))
14+
api(platform(libs.opentelemetry.platform.alpha))
15+
api(libs.opentelemetry.api)
16+
17+
implementation(libs.opentelemetry.instrumentation.apiSemconv)
18+
implementation(libs.opentelemetry.api.incubator)
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package io.opentelemetry.instrumentation.library.log;
2+
3+
import androidx.annotation.NonNull;
4+
5+
import com.google.auto.service.AutoService;
6+
7+
import io.opentelemetry.android.instrumentation.AndroidInstrumentation;
8+
import io.opentelemetry.android.instrumentation.InstallationContext;
9+
import io.opentelemetry.instrumentation.library.log.internal.LogRecordBuilderCreator;
10+
11+
@AutoService(AndroidInstrumentation.class)
12+
public class AndroidLogInstrumentation implements AndroidInstrumentation {
13+
public static final String INSTRUMENTATION_NAME = "android-log";
14+
15+
@Override
16+
public void install(@NonNull InstallationContext ctx) {
17+
LogRecordBuilderCreator.configure(ctx);
18+
}
19+
20+
@NonNull
21+
@Override
22+
public String getName() {
23+
return INSTRUMENTATION_NAME;
24+
}
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
package io.opentelemetry.instrumentation.library.log;
2+
3+
import static io.opentelemetry.instrumentation.library.log.internal.LogRecordBuilderCreator.createLogRecordBuilder;
4+
import static io.opentelemetry.instrumentation.library.log.internal.LogRecordBuilderCreator.getEventName;
5+
import static io.opentelemetry.instrumentation.library.log.internal.LogRecordBuilderCreator.printStacktrace;
6+
7+
import android.util.Log;
8+
9+
import io.opentelemetry.api.common.Attributes;
10+
import io.opentelemetry.api.logs.Severity;
11+
12+
public class AndroidLogSubstitutions {
13+
14+
public static String TAG_KEY = "android.tag";
15+
16+
public static String MESSAGE_KEY = "android.message";
17+
18+
public static int substitutionForVerbose(String tag, String message) {
19+
createLogRecordBuilder()
20+
.setSeverity(Severity.TRACE)
21+
.setAllAttributes(Attributes.builder()
22+
.put(TAG_KEY, tag)
23+
.build())
24+
.setBody(message)
25+
.emit();
26+
27+
return Log.v(tag, message);
28+
}
29+
30+
public static int substitutionForVerbose2(String tag, String message, Throwable throwable) {
31+
createLogRecordBuilder()
32+
.setEventName(getEventName(throwable))
33+
.setSeverity(Severity.TRACE)
34+
.setAllAttributes(Attributes.builder()
35+
.put(TAG_KEY, tag)
36+
.put(MESSAGE_KEY, message)
37+
.build())
38+
.setBody(printStacktrace(throwable))
39+
.emit();
40+
41+
return Log.v(tag, message, throwable);
42+
}
43+
44+
public static int substitutionForDebug(String tag, String message) {
45+
createLogRecordBuilder()
46+
.setSeverity(Severity.DEBUG)
47+
.setAllAttributes(Attributes.builder()
48+
.put(TAG_KEY, tag)
49+
.build())
50+
.setBody(message)
51+
.emit();
52+
53+
return Log.d(tag, message);
54+
}
55+
56+
public static int substitutionForDebug2(String tag, String message, Throwable throwable) {
57+
createLogRecordBuilder()
58+
.setEventName(getEventName(throwable))
59+
.setSeverity(Severity.DEBUG)
60+
.setAllAttributes(Attributes.builder()
61+
.put(TAG_KEY, tag)
62+
.put(MESSAGE_KEY, message)
63+
.build())
64+
.setBody(printStacktrace(throwable))
65+
.emit();
66+
67+
return Log.d(tag, message, throwable);
68+
}
69+
70+
public static int substitutionForInfo(String tag, String message) {
71+
createLogRecordBuilder()
72+
.setSeverity(Severity.INFO)
73+
.setAllAttributes(Attributes.builder()
74+
.put(TAG_KEY, tag)
75+
.build())
76+
.setBody(message)
77+
.emit();
78+
79+
return Log.i(tag, message);
80+
}
81+
82+
public static int substitutionForInfo2(String tag, String message, Throwable throwable) {
83+
createLogRecordBuilder()
84+
.setEventName(getEventName(throwable))
85+
.setSeverity(Severity.INFO)
86+
.setAllAttributes(Attributes.builder()
87+
.put(TAG_KEY, tag)
88+
.put(MESSAGE_KEY, message)
89+
.build())
90+
.setBody(printStacktrace(throwable))
91+
.emit();
92+
93+
return Log.i(tag, message, throwable);
94+
}
95+
96+
public static int substitutionForWarn(String tag, String message) {
97+
createLogRecordBuilder()
98+
.setSeverity(Severity.WARN)
99+
.setAllAttributes(Attributes.builder()
100+
.put(TAG_KEY, tag)
101+
.build())
102+
.setBody(message)
103+
.emit();
104+
105+
return Log.w(tag, message);
106+
}
107+
108+
public static int substitutionForWarn2(String tag, Throwable throwable) {
109+
createLogRecordBuilder()
110+
.setEventName(getEventName(throwable))
111+
.setSeverity(Severity.WARN)
112+
.setAllAttributes(Attributes.builder()
113+
.put(TAG_KEY, tag)
114+
.build())
115+
.setBody(printStacktrace(throwable))
116+
.emit();
117+
118+
return Log.w(tag, throwable);
119+
}
120+
121+
public static int substitutionForWarn3(String tag, String message, Throwable throwable) {
122+
createLogRecordBuilder()
123+
.setEventName(getEventName(throwable))
124+
.setSeverity(Severity.WARN)
125+
.setAllAttributes(Attributes.builder()
126+
.put(TAG_KEY, tag)
127+
.put(MESSAGE_KEY, message)
128+
.build())
129+
.setBody(printStacktrace(throwable))
130+
.emit();
131+
132+
return Log.w(tag, message, throwable);
133+
}
134+
135+
public static int substitutionForError(String tag, String message) {
136+
createLogRecordBuilder()
137+
.setSeverity(Severity.ERROR)
138+
.setAllAttributes(Attributes.builder()
139+
.put(TAG_KEY, tag)
140+
.build())
141+
.setBody(message)
142+
.emit();
143+
144+
return Log.e(tag, message);
145+
}
146+
147+
public static int substitutionForError2(String tag, String message, Throwable throwable) {
148+
createLogRecordBuilder()
149+
.setEventName(getEventName(throwable))
150+
.setSeverity(Severity.ERROR)
151+
.setAllAttributes(Attributes.builder()
152+
.put(TAG_KEY, tag)
153+
.put(MESSAGE_KEY, message)
154+
.build())
155+
.setBody(printStacktrace(throwable))
156+
.emit();
157+
158+
return Log.e(tag, message, throwable);
159+
}
160+
161+
public static int substitutionForWtf(String tag, String message) {
162+
createLogRecordBuilder()
163+
.setSeverity(Severity.UNDEFINED_SEVERITY_NUMBER)
164+
.setAllAttributes(Attributes.builder()
165+
.put(TAG_KEY, tag)
166+
.build())
167+
.setBody(message)
168+
.emit();
169+
170+
return Log.wtf(tag, message);
171+
}
172+
173+
public static int substitutionForWtf2(String tag, Throwable throwable) {
174+
createLogRecordBuilder()
175+
.setEventName(getEventName(throwable))
176+
.setSeverity(Severity.UNDEFINED_SEVERITY_NUMBER)
177+
.setAllAttributes(Attributes.builder()
178+
.put(TAG_KEY, tag)
179+
.build())
180+
.setBody(printStacktrace(throwable))
181+
.emit();
182+
183+
return Log.wtf(tag, throwable);
184+
}
185+
186+
public static int substitutionForWtf3(String tag, String message, Throwable throwable) {
187+
createLogRecordBuilder()
188+
.setEventName(getEventName(throwable))
189+
.setSeverity(Severity.UNDEFINED_SEVERITY_NUMBER)
190+
.setAllAttributes(Attributes.builder()
191+
.put(TAG_KEY, tag)
192+
.put(MESSAGE_KEY, message)
193+
.build())
194+
.setBody(printStacktrace(throwable))
195+
.emit();
196+
197+
return Log.wtf(tag, message, throwable);
198+
}
199+
}

0 commit comments

Comments
 (0)