Skip to content

Commit a79c467

Browse files
authored
Improve stack traces, fix mac build arch (#295)
1 parent d3bb24b commit a79c467

File tree

7 files changed

+210
-57
lines changed

7 files changed

+210
-57
lines changed

.github/workflows/release.yml

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ jobs:
9292
VERSION=$(node -p "require('./package.json').version")
9393
NODE_VERSION=${{ matrix.node-version }}
9494
NODE_MAJOR=$(echo $NODE_VERSION | cut -d. -f1)
95-
95+
9696
TAG=${{ needs.version-and-tag.outputs.tag }}
9797
if [[ "$TAG" =~ -alpha$ ]]; then
9898
FILE_NAME="${APP_NAME}-${VERSION}-alpha-linux-${{ matrix.arch }}-node${NODE_MAJOR}"
@@ -101,7 +101,7 @@ jobs:
101101
else
102102
FILE_NAME="${APP_NAME}-${VERSION}-linux-${{ matrix.arch }}-node${NODE_MAJOR}"
103103
fi
104-
104+
105105
ASSET_NAME=$(echo "$FILE_NAME" | tr '[:upper:]' '[:lower:]')
106106
echo "ASSET_NAME=${ASSET_NAME}" >> $GITHUB_OUTPUT
107107
@@ -117,17 +117,17 @@ jobs:
117117
/bin/bash -c '
118118
set -ex
119119
yum install -y make gcc-c++ python3 tar gzip wget tree
120-
120+
121121
wget -q https://go.dev/dl/go${{ needs.get-configs.outputs.go-version }}.linux-${{ matrix.go-arch }}.tar.gz
122122
tar -C /usr/local -xzf go${{ needs.get-configs.outputs.go-version }}.linux-${{ matrix.go-arch }}.tar.gz
123123
export PATH=$PATH:/usr/local/go/bin
124-
124+
125125
curl -fsSL https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash
126126
export NVM_DIR="$HOME/.nvm"
127127
. "$NVM_DIR/nvm.sh"
128128
nvm install ${{ matrix.node-version }}
129129
nvm use ${{ matrix.node-version }}
130-
130+
131131
npm ci
132132
133133
TAG="${{ needs.version-and-tag.outputs.tag }}"
@@ -138,10 +138,10 @@ jobs:
138138
else
139139
npm run bundle:prod
140140
fi
141-
141+
142142
tree -f bundle/production/node_modules/tree-sitter
143143
tree -f bundle/production/node_modules/tree-sitter-json
144-
144+
145145
GOARCH=${{ matrix.go-arch }} go build -C ./cfn-init/cmd -v -o ../../bundle/production/bin/cfn-init
146146
cp ./cfn-init/THIRD-PARTY-LICENSES.txt ./bundle/production/bin/
147147
'
@@ -161,7 +161,7 @@ jobs:
161161
fail-fast: true
162162
matrix:
163163
include:
164-
- { os: "macos-latest", arch: "x64", platform: "darwin", go-arch: "amd64", node-version: "22.x" }
164+
- { os: "macos-15-intel", arch: "x64", platform: "darwin", go-arch: "amd64", node-version: "22.x" }
165165
- { os: "macos-latest", arch: "arm64", platform: "darwin", go-arch: "arm64", node-version: "22.x" }
166166
- { os: "windows-latest", arch: "x64", platform: "win32", go-arch: "amd64", node-version: "22.x" }
167167
- { os: "windows-11-arm", arch: "arm64", platform: "win32", go-arch: "arm64", node-version: "22.x" }

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@aws/cloudformation-languageserver",
33
"displayName": "AWS CloudFormation",
4-
"version": "1.0.0",
4+
"version": "1.1.0",
55
"id": "aws-cloudformation-languageserver",
66
"description": "AWS CloudFormation Language Server implementation",
77
"publisher": "aws",

src/telemetry/LoggerFactory.ts

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { TelemetrySettings } from './TelemetryConfig';
1111

1212
export class LoggerFactory implements Closeable {
1313
private static readonly MaxFileSize = 50 * 1024 * 1024; // 50MB
14+
private static readonly FileName = `${ExtensionId}-${DateTime.utc().toFormat('yyyy-MM-dd')}.log`;
1415

1516
private static _instance: LoggerFactory;
1617

@@ -43,10 +44,7 @@ export class LoggerFactory implements Closeable {
4344
{
4445
target: 'pino/file',
4546
options: {
46-
destination: join(
47-
this.logsDirectory,
48-
`${ExtensionId}-${DateTime.utc().toFormat('yyyy-MM-dd')}.log`,
49-
),
47+
destination: join(this.logsDirectory, LoggerFactory.FileName),
5048
mkdir: true,
5149
},
5250
},
@@ -74,17 +72,13 @@ export class LoggerFactory implements Closeable {
7472
const oneWeekAgo = DateTime.utc().minus({ weeks: 1 });
7573

7674
for (const file of files) {
77-
if (!file.endsWith('.log')) continue;
78-
7975
const filePath = join(this.logsDirectory, file);
8076
const stats = await stat(filePath);
8177

82-
if (DateTime.fromJSDate(stats.mtime) < oneWeekAgo) {
78+
if (file !== LoggerFactory.FileName && DateTime.fromJSDate(stats.mtime) < oneWeekAgo) {
8379
await unlink(filePath);
8480
}
8581
}
86-
87-
await this.trimLogs();
8882
} catch (err) {
8983
this.baseLogger.error(err, 'Error cleaning up old logs');
9084
}

src/telemetry/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ Metrics includes the following metadata:
4848
| `RequestId` | Random unique identifier for each operation (UUID) | `5555-6666-7777-8888` |
4949

5050
## Data Transmission
51-
Metrics export every 30 seconds via HTTPS with TLS 1.2+ encryption using OpenTelemetry Protocol (OTLP).
51+
Metrics export every 60 seconds via HTTPS with TLS 1.2+ encryption using OpenTelemetry Protocol (OTLP).
52+
All metrics are stored in AWS [us-east-1](https://docs.aws.amazon.com/global-infrastructure/latest/regions/aws-regions.html) region
5253

5354
## Enable or Disable Telemetry
5455
Your LSP client (AWS ToolKit, VSCode, JetBrains, etc.) controls telemetry enablement via initialization options. For example:

src/utils/Errors.ts

Lines changed: 67 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import os from 'os';
2+
import { resolve, basename } from 'path';
13
import { ErrorCodes, ResponseError } from 'vscode-languageserver';
24
import { toString } from './String';
35

@@ -20,30 +22,77 @@ export function handleLspError(error: unknown, contextMessage: string): never {
2022
throw new ResponseError(ErrorCodes.InternalError, `${contextMessage}: ${extractErrorMessage(error)}`);
2123
}
2224

25+
const UserInfo = os.userInfo();
26+
const SensitiveInfo = [
27+
UserInfo.username,
28+
`${UserInfo.uid}`,
29+
`${UserInfo.gid}`,
30+
UserInfo.shell,
31+
UserInfo.homedir,
32+
resolve(__dirname),
33+
]
34+
.filter((v): v is string => typeof v === 'string' && v.length > 0)
35+
.sort((a, b) => b.length - a.length);
36+
37+
const BaseDir = basename(resolve(__dirname));
38+
39+
function sanitizePath(path: string): string {
40+
let sanitized = path;
41+
42+
// Strip sensitive info first
43+
for (const info of SensitiveInfo) {
44+
sanitized = sanitized.replaceAll(info, 'REDACTED');
45+
}
46+
47+
// Normalize path separators for consistent processing
48+
const normalized = sanitized.replaceAll('\\', '/');
49+
50+
// Strip cloudformation-languageserver prefix
51+
for (const partial of ['cloudformation-languageserver', BaseDir]) {
52+
const idx = normalized.indexOf(partial);
53+
if (idx !== -1) {
54+
return '/' + normalized.slice(idx + partial.length + 1);
55+
}
56+
}
57+
58+
// Restore original separators if no prefix found
59+
return sanitized;
60+
}
61+
2362
/**
2463
* Best effort extraction of location of exception based on stack trace
2564
*/
26-
export function extractLocationFromStack(stack?: string): {
27-
'error.file'?: string;
28-
'error.line'?: number;
29-
'error.column'?: number;
30-
} {
65+
export function extractLocationFromStack(stack?: string): Record<string, string> {
3166
if (!stack) return {};
3267

33-
// Match first line with file location: at ... (/path/to/file.ts:line:column)
34-
const match = stack.match(/at .+\((.+):(\d+):(\d+)\)|at (.+):(\d+):(\d+)/);
35-
if (!match) return {};
68+
const matches = [...stack.matchAll(/at (.*)/g)];
69+
70+
if (matches.length === 0) return {};
71+
72+
const result: Record<string, string> = {};
3673

37-
const fullPath = match[1] || match[4];
38-
const line = parseInt(match[2] || match[5], 10); // eslint-disable-line unicorn/prefer-number-properties
39-
const column = parseInt(match[3] || match[6], 10); // eslint-disable-line unicorn/prefer-number-properties
74+
for (const [index, match] of matches.entries()) {
75+
if (!match[1]) {
76+
continue;
77+
}
4078

41-
// Extract only filename without path
42-
const filename = fullPath?.split('/').pop()?.split('\\').pop();
79+
let line = match[1].trim();
80+
if (!line) {
81+
continue;
82+
}
83+
84+
// Extract function name and path separately
85+
const parenMatch = line.match(/^(.+?)\s+\((.+)\)$/);
86+
if (parenMatch) {
87+
const funcName = parenMatch[1];
88+
const path = sanitizePath(parenMatch[2]);
89+
line = `${funcName} (${path})`;
90+
} else {
91+
line = sanitizePath(line);
92+
}
93+
94+
result[`stack${index}`] = line.trim();
95+
}
4396

44-
return {
45-
'error.file': filename,
46-
'error.line': line,
47-
'error.column': column,
48-
};
97+
return result;
4998
}

0 commit comments

Comments
 (0)