mirror of
https://github.com/tdurieux/anonymous_github.git
synced 2026-05-30 12:39:28 +02:00
improve logging
This commit is contained in:
Vendored
+1
-1
File diff suppressed because one or more lines are too long
@@ -5053,7 +5053,11 @@ body {
|
|||||||
color: var(--color);
|
color: var(--color);
|
||||||
max-height: 26em;
|
max-height: 26em;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
white-space: pre;
|
/* pre-wrap honors leading-space indentation (so aligned columns survive)
|
||||||
|
while still wrapping long string values; overflow-wrap forces the break
|
||||||
|
mid-word for unbroken URLs / encoded JSON detail strings. */
|
||||||
|
white-space: pre-wrap;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
box-shadow: var(--card-shadow);
|
box-shadow: var(--card-shadow);
|
||||||
}
|
}
|
||||||
|
|||||||
+36
-7
@@ -916,7 +916,10 @@ angular
|
|||||||
// Decorate each entry once with derived display fields. Pre-computing
|
// Decorate each entry once with derived display fields. Pre-computing
|
||||||
// avoids returning new arrays from template functions each digest
|
// avoids returning new arrays from template functions each digest
|
||||||
// cycle (which trips Angular's $rootScope:infdig).
|
// cycle (which trips Angular's $rootScope:infdig).
|
||||||
const errorKeyRe = /^[a-z][a-z0-9]*(?:_[a-z0-9]+)+$/;
|
// snake_case-ish identifier looking like an error key. Accepts both
|
||||||
|
// pure lowercase ("repo_not_found") and the mixed-case style this
|
||||||
|
// codebase uses ("repoId_already_used", "invalid_repoId").
|
||||||
|
const errorKeyRe = /^[a-zA-Z][a-zA-Z0-9]*(?:_[a-zA-Z0-9]+)+$/;
|
||||||
function bucketFor(detail, level) {
|
function bucketFor(detail, level) {
|
||||||
const s =
|
const s =
|
||||||
(detail && (detail.httpStatus || detail.status)) || null;
|
(detail && (detail.httpStatus || detail.status)) || null;
|
||||||
@@ -971,12 +974,26 @@ angular
|
|||||||
};
|
};
|
||||||
push("name", detail && detail.name);
|
push("name", detail && detail.name);
|
||||||
push("code", entry.displayMessage || (detail && detail.message));
|
push("code", entry.displayMessage || (detail && detail.message));
|
||||||
// "kind" is a friendly grouping; only emit if we know the bucket.
|
|
||||||
if (entry._bucket) push("kind", entry._bucket);
|
if (entry._bucket) push("kind", entry._bucket);
|
||||||
push("httpStatus", detail && detail.httpStatus);
|
push("httpStatus", detail && detail.httpStatus);
|
||||||
if (detail && detail.status && !(detail.httpStatus)) push("status", detail.status);
|
if (detail && detail.status && !(detail.httpStatus)) push("status", detail.status);
|
||||||
push("module", entry.module);
|
push("module", entry.module);
|
||||||
push("detail", detail && detail.detail);
|
// AnonymousError.detail() can return a JSON-encoded string for
|
||||||
|
// structured payloads (e.g. {"repoId":"...","terms":[],"fullName":...}).
|
||||||
|
// Try to parse it so the renderer can pretty-print it multi-line
|
||||||
|
// instead of dumping an unreadable escape-soup blob.
|
||||||
|
let detailValue = detail && detail.detail;
|
||||||
|
if (typeof detailValue === "string") {
|
||||||
|
const trimmed = detailValue.trim();
|
||||||
|
if (trimmed[0] === "{" || trimmed[0] === "[") {
|
||||||
|
try {
|
||||||
|
detailValue = JSON.parse(detailValue);
|
||||||
|
} catch {
|
||||||
|
/* leave as string */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
push("detail", detailValue);
|
||||||
push("url", entry._url);
|
push("url", entry._url);
|
||||||
push("ts", entry.ts);
|
push("ts", entry.ts);
|
||||||
if (!fields.length) return JSON.stringify(entry, null, 2);
|
if (!fields.length) return JSON.stringify(entry, null, 2);
|
||||||
@@ -984,11 +1001,23 @@ angular
|
|||||||
const lines = ["{"];
|
const lines = ["{"];
|
||||||
fields.forEach(([k, v], i) => {
|
fields.forEach(([k, v], i) => {
|
||||||
const key = `"${k}":`.padEnd(keyW + 3, " ");
|
const key = `"${k}":`.padEnd(keyW + 3, " ");
|
||||||
const val = typeof v === "number" || typeof v === "boolean"
|
const prefix = ` ${key} `;
|
||||||
? String(v)
|
|
||||||
: JSON.stringify(v);
|
|
||||||
const comma = i < fields.length - 1 ? "," : "";
|
const comma = i < fields.length - 1 ? "," : "";
|
||||||
lines.push(` ${key} ${val}${comma}`);
|
let val;
|
||||||
|
if (v && typeof v === "object") {
|
||||||
|
// Indent continuation lines under the value column so the nested
|
||||||
|
// object reads like a column instead of breaking flow.
|
||||||
|
const pad = " ".repeat(prefix.length);
|
||||||
|
val = JSON.stringify(v, null, 2)
|
||||||
|
.split("\n")
|
||||||
|
.map((ln, idx) => (idx === 0 ? ln : pad + ln))
|
||||||
|
.join("\n");
|
||||||
|
} else if (typeof v === "number" || typeof v === "boolean") {
|
||||||
|
val = String(v);
|
||||||
|
} else {
|
||||||
|
val = JSON.stringify(v);
|
||||||
|
}
|
||||||
|
lines.push(`${prefix}${val}${comma}`);
|
||||||
});
|
});
|
||||||
lines.push("}");
|
lines.push("}");
|
||||||
return lines.join("\n");
|
return lines.join("\n");
|
||||||
|
|||||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
@@ -171,7 +171,7 @@ export default class AnonymizedFile {
|
|||||||
if (this._file?.size && this._file?.size > config.MAX_FILE_SIZE) {
|
if (this._file?.size && this._file?.size > config.MAX_FILE_SIZE) {
|
||||||
throw new AnonymousError("file_too_big", {
|
throw new AnonymousError("file_too_big", {
|
||||||
object: this,
|
object: this,
|
||||||
httpStatus: 403,
|
httpStatus: 413,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const content = await this.repository.source?.getFileContent(this);
|
const content = await this.repository.source?.getFileContent(this);
|
||||||
|
|||||||
@@ -59,6 +59,30 @@ export default class AnonymousError extends CustomError {
|
|||||||
return `GHDownload ${this.value.data.repoId}`;
|
return `GHDownload ${this.value.data.repoId}`;
|
||||||
}
|
}
|
||||||
if (typeof this.value === "string") return this.value;
|
if (typeof this.value === "string") return this.value;
|
||||||
|
// For plain objects (typically request bodies passed in by route
|
||||||
|
// handlers), pull out the diagnostic fingerprint instead of dumping
|
||||||
|
// the entire body. Routes throw with `object: req.body`, which used
|
||||||
|
// to bloat the log with the full JSON of options/source/etc — none
|
||||||
|
// of which helps an operator triage the failure.
|
||||||
|
if (typeof this.value === "object") {
|
||||||
|
const v = this.value as Record<string, unknown>;
|
||||||
|
const fingerprint: string[] = [];
|
||||||
|
if (typeof v.repoId === "string") fingerprint.push(`repoId=${v.repoId}`);
|
||||||
|
if (typeof v.fullName === "string") fingerprint.push(`fullName=${v.fullName}`);
|
||||||
|
if (typeof v.repositoryFullName === "string")
|
||||||
|
fingerprint.push(`pr=${v.repositoryFullName}`);
|
||||||
|
if (typeof v.pullRequestId === "string" || typeof v.pullRequestId === "number")
|
||||||
|
fingerprint.push(`prId=${v.pullRequestId}`);
|
||||||
|
if (typeof v.gistId === "string") fingerprint.push(`gistId=${v.gistId}`);
|
||||||
|
if (typeof v.username === "string") fingerprint.push(`user=${v.username}`);
|
||||||
|
if (typeof v.conferenceID === "string")
|
||||||
|
fingerprint.push(`conference=${v.conferenceID}`);
|
||||||
|
if (fingerprint.length) return fingerprint.join(" ");
|
||||||
|
// Fall back to a bounded, readable preview rather than a giant
|
||||||
|
// escaped JSON blob. Keep it small so the rendered card stays tidy.
|
||||||
|
const json = JSON.stringify(this.value);
|
||||||
|
return json.length > 120 ? json.slice(0, 117) + "…" : json;
|
||||||
|
}
|
||||||
return JSON.stringify(this.value);
|
return JSON.stringify(this.value);
|
||||||
} catch {
|
} catch {
|
||||||
return String(this.value);
|
return String(this.value);
|
||||||
|
|||||||
+1
-1
@@ -154,7 +154,7 @@ export default class Gist {
|
|||||||
) {
|
) {
|
||||||
throw new AnonymousError("gist_not_ready", {
|
throw new AnonymousError("gist_not_ready", {
|
||||||
object: this,
|
object: this,
|
||||||
httpStatus: 503,
|
httpStatus: 425,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -168,7 +168,7 @@ export default class PullRequest {
|
|||||||
) {
|
) {
|
||||||
throw new AnonymousError("pull_request_not_ready", {
|
throw new AnonymousError("pull_request_not_ready", {
|
||||||
object: this,
|
object: this,
|
||||||
httpStatus: 503,
|
httpStatus: 425,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -220,7 +220,7 @@ export default class Repository {
|
|||||||
) {
|
) {
|
||||||
throw new AnonymousError("repository_not_ready", {
|
throw new AnonymousError("repository_not_ready", {
|
||||||
object: this,
|
object: this,
|
||||||
httpStatus: 503,
|
httpStatus: 425,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ export class GitHubRepository {
|
|||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new AnonymousError("repo_not_found", {
|
throw new AnonymousError("repo_not_found", {
|
||||||
httpStatus: (error as { status?: number }).status,
|
httpStatus: (error as { status?: number }).status || 404,
|
||||||
cause: error as Error,
|
cause: error as Error,
|
||||||
object: this,
|
object: this,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -332,7 +332,7 @@ export default class GitHubStream extends GitHubBase {
|
|||||||
}
|
}
|
||||||
logger.warn("getTree failed", serializeError(error));
|
logger.warn("getTree failed", serializeError(error));
|
||||||
throw new AnonymousError("repo_not_found", {
|
throw new AnonymousError("repo_not_found", {
|
||||||
httpStatus: status || 500,
|
httpStatus: status || 404,
|
||||||
object: this.data,
|
object: this.data,
|
||||||
cause: error as Error,
|
cause: error as Error,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ router.get(
|
|||||||
if (!(await repo.isReady())) {
|
if (!(await repo.isReady())) {
|
||||||
throw new AnonymousError("repository_not_ready", {
|
throw new AnonymousError("repository_not_ready", {
|
||||||
object: repo,
|
object: repo,
|
||||||
httpStatus: 503,
|
httpStatus: 425,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const f = new AnonymizedFile({
|
const f = new AnonymizedFile({
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ router.get(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
throw new AnonymousError("gist_not_ready", {
|
throw new AnonymousError("gist_not_ready", {
|
||||||
httpStatus: 404,
|
httpStatus: 425,
|
||||||
object: gist,
|
object: gist,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -207,7 +207,7 @@ router.get(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
throw new AnonymousError("repository_not_ready", {
|
throw new AnonymousError("repository_not_ready", {
|
||||||
httpStatus: 404,
|
httpStatus: 425,
|
||||||
object: repo,
|
object: repo,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ export function isOwnerCoauthorOrAdmin(repo: Repository, user: User) {
|
|||||||
if (repo.owner.id === user.model.id) return;
|
if (repo.owner.id === user.model.id) return;
|
||||||
if (isCoauthor(repo, user)) return;
|
if (isCoauthor(repo, user)) return;
|
||||||
throw new AnonymousError("not_authorized", {
|
throw new AnonymousError("not_authorized", {
|
||||||
httpStatus: 401,
|
httpStatus: 403,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user