Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit2285279

Browse files
committed
Improve logging and put it in a separate file
1 parent2f3db57 commit2285279

File tree

8 files changed

+215
-80
lines changed

8 files changed

+215
-80
lines changed

‎src/api.ts‎

Lines changed: 18 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import{AxiosInstance,InternalAxiosRequestConfig,isAxiosError}from"axios";
1+
import{AxiosInstance}from"axios";
22
import{spawn}from"child_process";
33
import{Api}from"coder/site/src/api/api";
44
import{Workspace}from"coder/site/src/api/typesGenerated";
@@ -9,6 +9,13 @@ import { errToStr } from "./api-helper";
99
import{CertificateError}from"./error";
1010
import{FeatureSet}from"./featureSet";
1111
import{getGlobalFlags}from"./globalFlags";
12+
import{
13+
createRequestMeta,
14+
logRequestStart,
15+
logRequestSuccess,
16+
logRequestError,
17+
RequestConfigWithMeta,
18+
}from"./logging/netLog";
1219
import{getProxyForUrl}from"./proxy";
1320
import{Storage}from"./storage";
1421
import{expandPath}from"./util";
@@ -116,72 +123,33 @@ export function makeCoderSdk(
116123
returnrestClient;
117124
}
118125

119-
interfaceRequestConfigWithMetadataextendsInternalAxiosRequestConfig{
120-
metadata?:{
121-
requestId:string;
122-
startedAt:number;
123-
};
124-
}
125-
126-
functionaddLoggingInterceptors(
126+
exportfunctionaddLoggingInterceptors(
127127
client:AxiosInstance,
128128
logger:vscode.LogOutputChannel,
129129
){
130130
client.interceptors.request.use(
131131
(config)=>{
132-
constrequestId=crypto.randomUUID().replace(/-/g,"");
133-
(configasRequestConfigWithMetadata).metadata={
134-
requestId,
135-
startedAt:Date.now(),
136-
};
137-
138-
logger.trace(
139-
`req${requestId}:${config.method?.toUpperCase()}${config.url}`,
140-
config.data??"",
141-
);
142-
132+
constmeta=createRequestMeta();
133+
(configasRequestConfigWithMeta).metadata=meta;
134+
logRequestStart(logger,meta.requestId,config);
143135
returnconfig;
144136
},
145137
(error:unknown)=>{
146-
letmessage:string="Request error";
147-
if(isAxiosError(error)){
148-
constmeta=(error.configasRequestConfigWithMetadata)?.metadata;
149-
constrequestId=meta?.requestId??"n/a";
150-
message=`req${requestId} error`;
151-
}
152-
logger.error(message,error);
153-
138+
logRequestError(logger,error);
154139
returnPromise.reject(error);
155140
},
156141
);
157142

158143
client.interceptors.response.use(
159144
(response)=>{
160-
const{ requestId, startedAt}=
161-
(response.configasRequestConfigWithMetadata).metadata??{};
162-
constms=startedAt ?Date.now()-startedAt :undefined;
163-
164-
logger.trace(
165-
`res${requestId??"n/a"}:${response.status}${
166-
ms!==undefined ?` in${ms}ms` :""
167-
}`,
168-
// { responseBody: response.data }, // TODO too noisy
169-
);
145+
constmeta=(response.configasRequestConfigWithMeta).metadata;
146+
if(meta){
147+
logRequestSuccess(logger,meta,response);
148+
}
170149
returnresponse;
171150
},
172151
(error:unknown)=>{
173-
letmessage="Response error";
174-
if(isAxiosError(error)){
175-
const{ metadata}=(error.configasRequestConfigWithMetadata)??{};
176-
constrequestId=metadata?.requestId??"n/a";
177-
conststartedAt=metadata?.startedAt;
178-
constms=startedAt ?Date.now()-startedAt :undefined;
179-
180-
conststatus=error.response?.status??"unknown";
181-
message=`res${requestId}:${status}${ms!==undefined ?` in${ms}ms` :""}`;
182-
}
183-
logger.error(message,error);
184-
152+
logRequestError(logger,error);
185153
returnPromise.reject(error);
186154
},
187155
);

‎src/error.test.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import https from "https";
44
import*aspathfrom"path";
55
import{afterAll,beforeAll,it,expect,vi}from"vitest";
66
import{CertificateError,X509_ERR,X509_ERR_CODE}from"./error";
7-
import{Logger}from"./logger";
7+
import{Logger}from"./logging/logger";
88

99
// Before each test we make a request to sanity check that we really get the
1010
// error we are expecting, then we run it through CertificateError.

‎src/error.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { isApiError, isApiErrorResponse } from "coder/site/src/api/errors";
33
import*asforgefrom"node-forge";
44
import*astlsfrom"tls";
55
import*asvscodefrom"vscode";
6-
import{Logger}from"./logger";
6+
import{Logger}from"./logging/logger";
77

88
// X509_ERR_CODE represents error codes as returned from BoringSSL/OpenSSL.
99
exportenumX509_ERR_CODE{

‎src/headers.test.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import * as os from "os";
22
import{it,expect,describe,beforeEach,afterEach,vi}from"vitest";
33
import{WorkspaceConfiguration}from"vscode";
44
import{getHeaderCommand,getHeaders}from"./headers";
5-
import{Logger}from"./logger";
5+
import{Logger}from"./logging/logger";
66

77
constlogger:Logger={
88
trace:()=>{},

‎src/headers.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import * as cp from "child_process";
22
import*asosfrom"os";
33
import*asutilfrom"util";
44
importtype{WorkspaceConfiguration}from"vscode";
5-
import{Logger}from"./logger";
5+
import{Logger}from"./logging/logger";
66
import{escapeCommandArg}from"./util";
77

88
interfaceExecException{
File renamed without changes.

‎src/logging/netLog.ts‎

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
import{
2+
typeInternalAxiosRequestConfig,
3+
typeAxiosResponse,
4+
typeAxiosError,
5+
isAxiosError,
6+
}from"axios";
7+
import{Buffer}from"node:buffer";
8+
importcryptofrom"node:crypto";
9+
importtype*asvscodefrom"vscode";
10+
import{errToStr}from"../api-helper";
11+
12+
exportinterfaceRequestMeta{
13+
requestId:string;
14+
startedAt:number;
15+
}
16+
17+
exporttypeRequestConfigWithMeta=InternalAxiosRequestConfig&{
18+
metadata?:RequestMeta;
19+
};
20+
21+
functionshortId(id:string):string{
22+
returnid.slice(0,8);
23+
}
24+
25+
functionsizeOf(data:unknown):number{
26+
if(data===null||data===undefined){
27+
return0;
28+
}
29+
if(typeofdata==="string"){
30+
returnBuffer.byteLength(data);
31+
}
32+
if(Buffer.isBuffer(data)){
33+
returndata.length;
34+
}
35+
if(datainstanceofArrayBuffer||ArrayBuffer.isView(data)){
36+
returndata.byteLength;
37+
}
38+
if(
39+
typeofdata==="object"&&
40+
"size"indata&&
41+
typeofdata.size==="number"
42+
){
43+
returndata.size;
44+
}
45+
return0;
46+
}
47+
48+
exportfunctioncreateRequestMeta():RequestMeta{
49+
return{
50+
requestId:crypto.randomUUID().replace(/-/g,""),
51+
startedAt:Date.now(),
52+
};
53+
}
54+
55+
exportfunctionlogRequestStart(
56+
logger:vscode.LogOutputChannel,
57+
requestId:string,
58+
config:InternalAxiosRequestConfig,
59+
):void{
60+
constmethod=(config.method??"GET").toUpperCase();
61+
consturl=config.url||"";
62+
constlen=config.headers?.["content-length"]asstring|undefined;
63+
constlenStr=len ?` (${len}b)` :"";
64+
logger.trace(`→${shortId(requestId)}${method}${url}${lenStr}`);
65+
}
66+
67+
exportfunctionlogRequestSuccess(
68+
logger:vscode.LogOutputChannel,
69+
meta:RequestMeta,
70+
response:AxiosResponse,
71+
):void{
72+
constmethod=(response.config.method??"GET").toUpperCase();
73+
consturl=response.config.url||"";
74+
constlen=response.headers?.["content-length"]asstring|undefined;
75+
constms=Date.now()-meta.startedAt;
76+
constlenStr=len ?` (${len}b)` :"";
77+
logger.trace(
78+
`←${shortId(meta.requestId)}${response.status}${method}${url}${ms}ms${lenStr}`,
79+
);
80+
}
81+
82+
exportfunctionlogRequestError(
83+
logger:vscode.LogOutputChannel,
84+
error:AxiosError|unknown,
85+
):void{
86+
if(isAxiosError(error)){
87+
constconfig=error.configasRequestConfigWithMeta|undefined;
88+
constmeta=config?.metadata;
89+
constmethod=(config?.method??"GET").toUpperCase();
90+
consturl=config?.url||"";
91+
constrequestId=meta?.requestId??"unknown";
92+
constms=meta ?Date.now()-meta.startedAt :"?";
93+
94+
if(error.response){
95+
// Response error (4xx, 5xx status codes)
96+
constmsg=
97+
error.response.statusText||String(error.response.data).slice(0,100);
98+
logger.error(
99+
`←${shortId(requestId)}${error.response.status}${method}${url}${ms}ms -${msg}`,
100+
error,
101+
);
102+
}else{
103+
// Request error (network, timeout, etc)
104+
constreason=error.code||error.message||"Network error";
105+
logger.error(
106+
`✗${shortId(requestId)}${method}${url}${ms}ms -${reason}`,
107+
error,
108+
);
109+
}
110+
}else{
111+
logger.error("Request error",error);
112+
}
113+
}
114+
115+
exportclassWsLogger{
116+
privatelogger:vscode.LogOutputChannel;
117+
privateurl:string;
118+
privateid:string;
119+
privatestartedAt:number;
120+
privateopenedAt?:number;
121+
privatemsgCount=0;
122+
privatebyteCount=0;
123+
124+
constructor(logger:vscode.LogOutputChannel,url:string){
125+
this.logger=logger;
126+
this.url=url;
127+
this.id=crypto.randomUUID().replace(/-/g,"");
128+
this.startedAt=Date.now();
129+
}
130+
131+
logConnecting():void{
132+
this.logger.trace(`→ WS${shortId(this.id)}${this.url}`);
133+
}
134+
135+
logOpen():void{
136+
this.openedAt=Date.now();
137+
constconnectMs=this.openedAt-this.startedAt;
138+
this.logger.trace(`← WS${shortId(this.id)} connected${connectMs}ms`);
139+
}
140+
141+
logMessage(data:unknown):void{
142+
this.msgCount+=1;
143+
this.byteCount+=sizeOf(data);
144+
}
145+
146+
logClose(code?:number,reason?:string):void{
147+
constupMs=this.openedAt ?Date.now()-this.openedAt :0;
148+
conststats=[];
149+
if(upMs>0){
150+
stats.push(`${upMs}ms`);
151+
}
152+
if(this.msgCount>0){
153+
stats.push(`${this.msgCount} msgs`);
154+
}
155+
if(this.byteCount>0){
156+
stats.push(`${this.byteCount}b`);
157+
}
158+
159+
constcodeStr=code ?` (${code})` :"";
160+
constreasonStr=reason ?` -${reason}` :"";
161+
conststatsStr=stats.length>0 ?` [${stats.join(", ")}]` :"";
162+
163+
this.logger.trace(
164+
`✗ WS${shortId(this.id)} closed${codeStr}${reasonStr}${statsStr}`,
165+
);
166+
}
167+
168+
logError(error:unknown):void{
169+
constms=Date.now()-this.startedAt;
170+
consterrorMsg=errToStr(error,"connection error");
171+
this.logger.error(
172+
`✗ WS${shortId(this.id)} error${ms}ms -${errorMsg}`,
173+
error,
174+
);
175+
}
176+
}

‎src/websocket/webSocketClient.ts‎

Lines changed: 17 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
import{ProxyAgent}from"proxy-agent";
1010
import{ClientOptions}from"ws";
1111
import{coderSessionTokenHeader}from"../api";
12-
import{errToStr}from"../api-helper";
12+
import{WsLogger}from"../logging/netLog";
1313
import{Storage}from"../storage";
1414
import{OneWayCodeWebSocket}from"./oneWayCodeWebSocket";
1515

@@ -86,53 +86,44 @@ export class CoderWebSocketClient {
8686
thrownewError("No base URL set on REST client");
8787
}
8888

89-
// We shouldn't need to worry about this throwing. Whilst `baseURL` could
90-
// be an invalid URL, that would've caused issues before we got to here.
9189
constbaseUrl=newURL(baseUrlRaw);
9290
consttoken=this.client.getAxiosInstance().defaults.headers.common[
9391
coderSessionTokenHeader
9492
]asstring|undefined;
9593

96-
// Log WebSocket connection attempt
97-
this.storage.output.trace(
98-
`Creating WebSocket connection to${configs.apiRoute}`,
99-
);
100-
10194
constwebSocket=newOneWayCodeWebSocket<TData>({
10295
location:baseUrl,
10396
...configs,
10497
options:{
10598
agent:this.httpAgent,
10699
followRedirects:true,
107-
headers:token
108-
?{
109-
[coderSessionTokenHeader]:token,
110-
...configs.options?.headers,
111-
}
112-
:configs.options?.headers,
100+
headers:{
101+
...(token ?{[coderSessionTokenHeader]:token} :{}),
102+
...configs.options?.headers,
103+
},
113104
...configs.options,
114105
},
115106
});
116107

117-
// Add logging for WebSocket events
108+
constwsUrl=newURL(webSocket.url);
109+
constpathWithQuery=wsUrl.pathname+wsUrl.search;
110+
constwsLogger=newWsLogger(this.storage.output,pathWithQuery);
111+
wsLogger.logConnecting();
112+
118113
webSocket.addEventListener("open",()=>{
119-
this.storage.output.trace(
120-
`WebSocket connection opened to${configs.apiRoute}`,
121-
);
114+
wsLogger.logOpen();
115+
});
116+
117+
webSocket.addEventListener("message",(event)=>{
118+
wsLogger.logMessage(event.sourceEvent.data);
122119
});
123120

124121
webSocket.addEventListener("close",(event)=>{
125-
this.storage.output.trace(
126-
`WebSocket connection closed to${configs.apiRoute}, code:${event.code}, reason:${event.reason}`,
127-
);
122+
wsLogger.logClose(event.code,event.reason);
128123
});
129124

130125
webSocket.addEventListener("error",(event)=>{
131-
consterr=errToStr(
132-
event.error,
133-
`Got empty error while monitoring${configs.apiRoute}`,
134-
);
135-
this.storage.output.error(err);
126+
wsLogger.logError(event?.error??event);
136127
});
137128

138129
returnwebSocket;

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp