Skip to content

Commit e95907d

Browse files
authored
Feature/network logging (#82)
* ✨ network logging for HttpClient * ⬆️ update native android version to 8.5.0.1 * Update readme with details on network logging * 📝 Update changelog
1 parent 49f29ba commit e95907d

12 files changed

+827
-1
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## Version 1.0.0-beta.5 (2019-07-22)
2+
3+
* Adds Network logging feature for the dart:io package HttpClient.
4+
15
## Version 1.0.0-beta.4 (2019-06-25)
26

37
* Fixes crash on Android on launching the sample app.

README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,3 +201,19 @@ If your app doesn’t already access the microphone or photo library, we recomme
201201
* "`<app name>` needs access to your photo library for you to be able to attach images."
202202

203203
**The permission alert for accessing the microphone/photo library will NOT appear unless users attempt to attach a voice note/photo while using Instabug.**
204+
205+
## Network Logging
206+
You can choose to attach all your network requests to the reports being sent to the dashboard. To enable the feature when using the `dart:io` package `HttpClient`, use the custom Instabug client:
207+
```
208+
InstabugCustomHttpClient client = InstabugCustomHttpClient();
209+
```
210+
211+
and continue to use the package normally to make your network requests:
212+
213+
```
214+
client.getUrl(Uri.parse(URL)).then((request) async {
215+
var response = await request.close();
216+
});
217+
```
218+
219+
We also support the packages `http` and `dio`. For details on how to enable network logging for these external packages, refer to the [Instabug Dart Http Adapter](https://github.com/Instabug/Instabug-Dart-http-Adapter) and the [Instabug Dio Interceptor](https://github.com/Instabug/Instabug-Dio-Interceptor) repositories.

android/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,6 @@ android {
3434
}
3535

3636
dependencies {
37-
implementation 'com.instabug.library:instabug:8.4.0.2'
37+
implementation 'com.instabug.library:instabug:8.5.0.1'
3838
testImplementation 'junit:junit:4.12'
3939
}

android/src/main/java/com/instabug/instabugflutter/InstabugFlutterPlugin.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,16 @@
2020
import com.instabug.library.invocation.InstabugInvocationEvent;
2121
import com.instabug.library.invocation.OnInvokeCallback;
2222
import com.instabug.library.logging.InstabugLog;
23+
import com.instabug.library.model.NetworkLog;
2324
import com.instabug.library.ui.onboarding.WelcomeMessage;
2425
import com.instabug.survey.OnDismissCallback;
2526
import com.instabug.survey.OnShowCallback;
2627
import com.instabug.survey.Survey;
2728
import com.instabug.survey.Surveys;
2829

30+
import org.json.JSONException;
31+
import org.json.JSONObject;
32+
2933
import java.io.File;
3034
import java.lang.reflect.InvocationTargetException;
3135
import java.lang.reflect.Method;
@@ -855,4 +859,27 @@ public void setEnableInAppNotificationSound(boolean shouldPlaySound) {
855859
Replies.setInAppNotificationSound(shouldPlaySound);
856860
}
857861

862+
/**
863+
* Extracts HTTP connection properties. Request method, Headers, Date, Url and Response code
864+
*
865+
* @param jsonObject the JSON object containing all HTTP connection properties
866+
*/
867+
public void networkLog(HashMap<String, Object> jsonObject) throws JSONException {
868+
869+
int responseCode = 0;
870+
871+
NetworkLog networkLog = new NetworkLog();
872+
String date = System.currentTimeMillis()+"";
873+
networkLog.setDate(date);
874+
networkLog.setUrl((String)jsonObject.get("url"));
875+
networkLog.setRequest((String)jsonObject.get("requestBody"));
876+
networkLog.setResponse((String)jsonObject.get("responseBody"));
877+
networkLog.setMethod((String) jsonObject.get("method"));
878+
networkLog.setResponseCode((Integer) jsonObject.get("responseCode"));
879+
networkLog.setRequestHeaders((new JSONObject((HashMap<String, String>)jsonObject.get("requestHeaders"))).toString(4));
880+
networkLog.setResponseHeaders((new JSONObject((HashMap<String, String>)jsonObject.get("responseHeaders"))).toString(4));
881+
networkLog.setTotalDuration(((Number) jsonObject.get("duration")).longValue());
882+
networkLog.insert();
883+
}
884+
858885
}

ios/Classes/InstabugFlutterPlugin.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,4 +417,8 @@
417417
* @param isEnabled whether chat notification is reburied or not
418418
*/
419419
- (void)setChatNotificationEnabled:(NSNumber *)isEnabled ;
420+
421+
+ (void)networkLog:(NSDictionary *) networkData ;
422+
423+
420424
@end

ios/Classes/InstabugFlutterPlugin.m

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -707,6 +707,54 @@ + (void)setChatNotificationEnabled:(NSNumber *)isEnabled {
707707
IBGReplies.inAppNotificationsEnabled = boolValue;
708708
}
709709

710+
/**
711+
* Extracts HTTP connection properties. Request method, Headers, Date, Url and Response code
712+
*
713+
* @param networkData the NSDictionary containing all HTTP connection properties
714+
*/
715+
+ (void)networkLog:(NSDictionary *) networkData {
716+
[IBGLog clearAllLogs];
717+
718+
NSString* url = networkData[@"url"];
719+
NSString* method = networkData[@"method"];
720+
NSString* requestBody = networkData[@"requestBody"];
721+
NSString* responseBody = networkData[@"responseBody"];
722+
int32_t responseCode = [networkData[@"responseCode"] integerValue];
723+
NSDictionary* requestHeaders = networkData[@"requestHeaders"];
724+
if ([requestHeaders count] == 0) {
725+
requestHeaders = @{};
726+
}
727+
NSDictionary* responseHeaders = networkData[@"responseHeaders"];
728+
NSString* contentType = @"application/json";
729+
double duration = [networkData[@"duration"] doubleValue];
730+
731+
for(NSString *key in [requestHeaders allKeys]) {
732+
NSLog(@"key: %@", key);
733+
NSLog(@"value: %@",[requestHeaders objectForKey:key]);
734+
}
735+
736+
SEL networkLogSEL = NSSelectorFromString(@"addNetworkLogWithUrl:method:requestBody:responseBody:responseCode:requestHeaders:responseHeaders:contentType:duration:");
737+
738+
if([[IBGNetworkLogger class] respondsToSelector:networkLogSEL]) {
739+
NSInvocation *inv = [NSInvocation invocationWithMethodSignature:[[IBGNetworkLogger class] methodSignatureForSelector:networkLogSEL]];
740+
[inv setSelector:networkLogSEL];
741+
[inv setTarget:[IBGNetworkLogger class]];
742+
743+
[inv setArgument:&(url) atIndex:2];
744+
[inv setArgument:&(method) atIndex:3];
745+
[inv setArgument:&(requestBody) atIndex:4];
746+
[inv setArgument:&(responseBody) atIndex:5];
747+
[inv setArgument:&(responseCode) atIndex:6];
748+
[inv setArgument:&(requestHeaders) atIndex:7];
749+
[inv setArgument:&(responseHeaders) atIndex:8];
750+
[inv setArgument:&(contentType) atIndex:9];
751+
[inv setArgument:&(duration) atIndex:10];
752+
753+
[inv invoke];
754+
}
755+
}
756+
757+
710758

711759
+ (NSDictionary *)constants {
712760
return @{

lib/NetworkLogger.dart

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import 'dart:async';
2+
import 'dart:io';
3+
import 'package:flutter/services.dart';
4+
import 'package:instabug/models/network_data.dart';
5+
6+
class NetworkLogger {
7+
8+
static const MethodChannel _channel = MethodChannel('instabug_flutter');
9+
10+
static Future<String> get platformVersion async {
11+
final String version = await _channel.invokeMethod('getPlatformVersion');
12+
return version;
13+
}
14+
15+
static Future<bool> networkLog(NetworkData data) async {
16+
final List<dynamic> params = <dynamic>[data.toMap()];
17+
return await _channel.invokeMethod<Object>('networkLog:', params);
18+
}
19+
20+
}

lib/instabug_custom_http_client.dart

Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
import 'dart:io';
2+
3+
import 'package:instabug/utils/http_client_logger.dart';
4+
import 'package:meta/meta.dart';
5+
6+
class InstabugCustomHttpClient extends HttpClientLogger implements HttpClient {
7+
InstabugCustomHttpClient() {
8+
client = HttpClient();
9+
10+
logger = this;
11+
}
12+
13+
@override
14+
set autoUncompress(bool au) => client.autoUncompress = au;
15+
16+
@override
17+
set connectionTimeout(Duration ct) => client.connectionTimeout = ct;
18+
19+
@override
20+
set idleTimeout(Duration it) => client.idleTimeout = it;
21+
22+
@override
23+
set maxConnectionsPerHost(int mcph) => client.maxConnectionsPerHost = mcph;
24+
25+
@override
26+
set userAgent (String ua) => client.userAgent = ua;
27+
28+
@override
29+
bool get autoUncompress => client.autoUncompress;
30+
31+
@override
32+
Duration get connectionTimeout => client.connectionTimeout;
33+
34+
@override
35+
Duration get idleTimeout => client.idleTimeout;
36+
37+
@override
38+
int get maxConnectionsPerHost => client.maxConnectionsPerHost;
39+
40+
@override
41+
String get userAgent => client.userAgent;
42+
43+
@visibleForTesting
44+
HttpClient client;
45+
46+
@visibleForTesting
47+
HttpClientLogger logger;
48+
49+
@override
50+
void addCredentials(
51+
Uri url, String realm, HttpClientCredentials credentials) {
52+
client.addCredentials(url, realm, credentials);
53+
}
54+
55+
@override
56+
void addProxyCredentials(
57+
String host, int port, String realm, HttpClientCredentials credentials) {
58+
client.addProxyCredentials(host, port, realm, credentials);
59+
}
60+
61+
@override
62+
void set authenticate(
63+
Future<bool> Function(Uri url, String scheme, String realm) f) {
64+
client.authenticate = f;
65+
}
66+
67+
@override
68+
void set authenticateProxy(
69+
Future<bool> Function(String host, int port, String scheme, String realm)
70+
f) {
71+
client.authenticateProxy = f;
72+
}
73+
74+
@override
75+
void set badCertificateCallback(
76+
bool Function(X509Certificate cert, String host, int port) callback) {
77+
client.badCertificateCallback = callback;
78+
}
79+
80+
@override
81+
void close({bool force = false}) {
82+
client.close(force: force);
83+
}
84+
85+
@override
86+
Future<HttpClientRequest> delete(String host, int port, String path) {
87+
return client.delete(host, port, path).then((HttpClientRequest request) async {
88+
logger.onRequest(request);
89+
final HttpClientResponse response = await request.close();
90+
logger.onResponse(response, request);
91+
return request;
92+
});
93+
}
94+
95+
@override
96+
Future<HttpClientRequest> deleteUrl(Uri url) {
97+
return client.deleteUrl(url).then((HttpClientRequest request) async {
98+
logger.onRequest(request);
99+
final HttpClientResponse response = await request.close();
100+
logger.onResponse(response, request);
101+
return request;
102+
});
103+
}
104+
105+
@override
106+
void set findProxy(String Function(Uri url) f) {
107+
client.findProxy = f;
108+
}
109+
110+
@override
111+
Future<HttpClientRequest> get(String host, int port, String path) {
112+
return client.get(host, port, path).then((HttpClientRequest request) async {
113+
logger.onRequest(request);
114+
final HttpClientResponse response = await request.close();
115+
logger.onResponse(response, request);
116+
return request;
117+
});
118+
}
119+
120+
@override
121+
Future<HttpClientRequest> getUrl(Uri url) {
122+
return client.getUrl(url).then((HttpClientRequest request) async {
123+
logger.onRequest(request);
124+
final HttpClientResponse response = await request.close();
125+
logger.onResponse(response, request);
126+
return request;
127+
});
128+
}
129+
130+
@override
131+
Future<HttpClientRequest> head(String host, int port, String path) {
132+
return client.head(host, port, path).then((HttpClientRequest request) async {
133+
logger.onRequest(request);
134+
final HttpClientResponse response = await request.close();
135+
logger.onResponse(response, request);
136+
return request;
137+
});
138+
}
139+
140+
@override
141+
Future<HttpClientRequest> headUrl(Uri url) {
142+
return client.headUrl(url).then((HttpClientRequest request) async {
143+
logger.onRequest(request);
144+
final HttpClientResponse response = await request.close();
145+
logger.onResponse(response, request);
146+
return request;
147+
});
148+
}
149+
150+
@override
151+
Future<HttpClientRequest> open(
152+
String method, String host, int port, String path) {
153+
return client.open(method, host, port, path).then((HttpClientRequest request) async {
154+
logger.onRequest(request);
155+
final HttpClientResponse response = await request.close();
156+
logger.onResponse(response, request);
157+
return request;
158+
});
159+
}
160+
161+
@override
162+
Future<HttpClientRequest> openUrl(String method, Uri url) {
163+
return client.openUrl(method, url).then((HttpClientRequest request) async {
164+
logger.onRequest(request);
165+
final HttpClientResponse response = await request.close();
166+
logger.onResponse(response, request);
167+
return request;
168+
});
169+
}
170+
171+
@override
172+
Future<HttpClientRequest> patch(String host, int port, String path) {
173+
return client.patch(host, port, path).then((HttpClientRequest request) async {
174+
logger.onRequest(request);
175+
final HttpClientResponse response = await request.close();
176+
logger.onResponse(response, request);
177+
return request;
178+
});
179+
}
180+
181+
@override
182+
Future<HttpClientRequest> patchUrl(Uri url) {
183+
return client.patchUrl(url).then((HttpClientRequest request) async {
184+
logger.onRequest(request);
185+
final HttpClientResponse response = await request.close();
186+
logger.onResponse(response, request);
187+
return request;
188+
});
189+
}
190+
191+
@override
192+
Future<HttpClientRequest> post(String host, int port, String path) {
193+
return client.post(host, port, path).then((HttpClientRequest request) async {
194+
logger.onRequest(request);
195+
final HttpClientResponse response = await request.close();
196+
logger.onResponse(response, request);
197+
return request;
198+
});
199+
}
200+
201+
@override
202+
Future<HttpClientRequest> postUrl(Uri url) {
203+
return client.postUrl(url).then((HttpClientRequest request) async {
204+
logger.onRequest(request);
205+
final HttpClientResponse response = await request.close();
206+
logger.onResponse(response, request);
207+
return request;
208+
});
209+
}
210+
211+
@override
212+
Future<HttpClientRequest> put(String host, int port, String path) {
213+
return client.put(host, port, path).then((HttpClientRequest request) async {
214+
logger.onRequest(request);
215+
final HttpClientResponse response = await request.close();
216+
logger.onResponse(response, request);
217+
return request;
218+
});
219+
}
220+
221+
@override
222+
Future<HttpClientRequest> putUrl(Uri url) {
223+
return client.putUrl(url).then((HttpClientRequest request) async {
224+
logger.onRequest(request);
225+
final HttpClientResponse response = await request.close();
226+
logger.onResponse(response, request);
227+
return request;
228+
});
229+
}
230+
231+
232+
}

0 commit comments

Comments
 (0)