-
Notifications
You must be signed in to change notification settings - Fork 45
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Logging to Logtail inside the Netlify update-settings function #1265
base: dev
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
|
@@ -4,6 +4,10 @@ import { createClient } from "@supabase/supabase-js"; | |||||||
import { Database } from "../../src/types/supabase-notification"; | ||||||||
import messages from "../../src/consts/eip712-messages"; | ||||||||
import { EMAIL_REGEX, TELEGRAM_REGEX, ETH_ADDRESS_REGEX, ETH_SIGNATURE_REGEX } from "../../src/consts/index"; | ||||||||
import { createLogger, throwNewError } from "../../src/utils/logger"; | ||||||||
import dotenv from "dotenv"; | ||||||||
|
||||||||
dotenv.config(); | ||||||||
|
||||||||
type NotificationSettings = { | ||||||||
email?: string; | ||||||||
|
@@ -13,12 +17,15 @@ type NotificationSettings = { | |||||||
signature: string; | ||||||||
}; | ||||||||
|
||||||||
const logger = createLogger(process.env.LOGTAIL_TOKEN).child({ function: "update-settings" }); | ||||||||
const logAndThrowNewError = (message: string, error?: any) => throwNewError(logger, message, error); | ||||||||
|
||||||||
const parse = (inputString: string): NotificationSettings => { | ||||||||
let input; | ||||||||
try { | ||||||||
input = JSON.parse(inputString); | ||||||||
} catch (err) { | ||||||||
throw new Error("Invalid JSON format"); | ||||||||
logAndThrowNewError("Invalid JSON format", err); | ||||||||
} | ||||||||
|
||||||||
const requiredKeys: (keyof NotificationSettings)[] = ["nonce", "address", "signature"]; | ||||||||
|
@@ -27,37 +34,37 @@ const parse = (inputString: string): NotificationSettings => { | |||||||
|
||||||||
for (const key of requiredKeys) { | ||||||||
if (!receivedKeys.includes(key)) { | ||||||||
throw new Error(`Missing key: ${key}`); | ||||||||
logAndThrowNewError(`Missing key: ${key}`); | ||||||||
} | ||||||||
} | ||||||||
|
||||||||
const allExpectedKeys = [...requiredKeys, ...optionalKeys]; | ||||||||
for (const key of receivedKeys) { | ||||||||
if (!allExpectedKeys.includes(key as keyof NotificationSettings)) { | ||||||||
throw new Error(`Unexpected key: ${key}`); | ||||||||
logAndThrowNewError(`Unexpected key: ${key}`); | ||||||||
} | ||||||||
} | ||||||||
|
||||||||
const email = input.email ? input.email.trim() : ""; | ||||||||
if (email && !EMAIL_REGEX.test(email)) { | ||||||||
throw new Error("Invalid email format"); | ||||||||
logAndThrowNewError("Invalid email format"); | ||||||||
} | ||||||||
|
||||||||
const telegram = input.telegram ? input.telegram.trim() : ""; | ||||||||
if (telegram && !TELEGRAM_REGEX.test(telegram)) { | ||||||||
throw new Error("Invalid Telegram username format"); | ||||||||
logAndThrowNewError("Invalid Telegram username format"); | ||||||||
} | ||||||||
|
||||||||
if (!/^\d+$/.test(input.nonce)) { | ||||||||
throw new Error("Invalid nonce format. Expected an integer as a string."); | ||||||||
logAndThrowNewError("Invalid nonce format. Expected an integer as a string."); | ||||||||
} | ||||||||
|
||||||||
if (!ETH_ADDRESS_REGEX.test(input.address)) { | ||||||||
throw new Error("Invalid Ethereum address format"); | ||||||||
logAndThrowNewError("Invalid Ethereum address format"); | ||||||||
} | ||||||||
|
||||||||
if (!ETH_SIGNATURE_REGEX.test(input.signature)) { | ||||||||
throw new Error("Invalid signature format"); | ||||||||
logAndThrowNewError("Invalid signature format"); | ||||||||
} | ||||||||
|
||||||||
return { | ||||||||
|
@@ -72,7 +79,7 @@ const parse = (inputString: string): NotificationSettings => { | |||||||
export const handler: Handler = async (event) => { | ||||||||
try { | ||||||||
if (!event.body) { | ||||||||
throw new Error("No body provided"); | ||||||||
logAndThrowNewError("No body provided"); | ||||||||
} | ||||||||
const { email, telegram, nonce, address, signature } = parse(event.body); | ||||||||
const lowerCaseAddress = address.toLowerCase() as `0x${string}`; | ||||||||
|
@@ -85,6 +92,7 @@ export const handler: Handler = async (event) => { | |||||||
}); | ||||||||
if (!isValid) { | ||||||||
// If the recovered address does not match the provided address, return an error | ||||||||
logAndThrowNewError("Signature verification failed"); | ||||||||
throw new Error("Signature verification failed"); | ||||||||
Comment on lines
+95
to
96
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove redundant The Apply this diff to remove the redundant throw statement: logAndThrowNewError("Signature verification failed");
- throw new Error("Signature verification failed"); 📝 Committable suggestion
Suggested change
|
||||||||
} | ||||||||
|
||||||||
|
@@ -94,6 +102,7 @@ export const handler: Handler = async (event) => { | |||||||
if (email === "" && telegram === "") { | ||||||||
const { error } = await supabase.from("users").delete().match({ address: lowerCaseAddress }); | ||||||||
if (error) throw error; | ||||||||
logger.info("Record deleted successfully."); | ||||||||
return { statusCode: 200, body: JSON.stringify({ message: "Record deleted successfully." }) }; | ||||||||
} | ||||||||
|
||||||||
|
@@ -105,8 +114,12 @@ export const handler: Handler = async (event) => { | |||||||
if (error) { | ||||||||
throw error; | ||||||||
} | ||||||||
logger.info("Record updated successfully."); | ||||||||
return { statusCode: 200, body: JSON.stringify({ message: "Record updated successfully." }) }; | ||||||||
} catch (err) { | ||||||||
logger.error(err); | ||||||||
return { statusCode: 500, body: JSON.stringify({ message: `Error: ${err}` }) }; | ||||||||
} finally { | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Avoid returning internal error details in HTTP responses Returning internal error objects directly to the client can expose sensitive information or implementation details. It's safer to provide a generic error message to the client while logging the detailed error internally. Apply this diff to return a generic error message: logger.error(err);
-return { statusCode: 500, body: JSON.stringify({ message: `Error: ${err}` }) };
+return { statusCode: 500, body: JSON.stringify({ message: "An unexpected error occurred." }) };
|
||||||||
logger.flush(); | ||||||||
} | ||||||||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -70,6 +70,7 @@ | |
"@filebase/client": "^0.0.5", | ||
"@kleros/kleros-v2-contracts": "workspace:^", | ||
"@kleros/ui-components-library": "^2.6.2", | ||
"@logtail/pino": "^0.4.12", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Codebase verification Unused logging dependencies detected It appears that "@logtail/pino" is added as a dependency but is not being utilized in the codebase. Additionally, "pino-pretty" is included but not referenced in any npm scripts or configuration files. Please verify whether these dependencies are intended to be used and ensure they are properly integrated. If they are not required, consider removing them to maintain a clean and efficient dependency list. 🔗 Analysis chainNew logging dependencies added The addition of "@logtail/pino", "pino", and "pino-pretty" dependencies indicates an enhancement in the project's logging capabilities. This aligns with the PR objectives of improving error handling and logging. A few points to consider:
To ensure these new dependencies are being utilized in the project, let's run the following script: Also applies to: 91-92 🏁 Scripts executedThe following scripts were executed for the analysis: Script: #!/bin/bash
# Description: Verify the usage of new logging dependencies in the project
# Test: Search for import or require statements of the new logging libraries
echo "Searching for usage of new logging libraries:"
rg --type typescript --type javascript -e "from ['\"]@logtail/pino['\"]" -e "from ['\"]pino['\"]" -e "require\(['\"]@logtail/pino['\"]" -e "require\(['\"]pino['\"]"
# Test: Check if pino-pretty is used in any npm scripts
echo "Checking for pino-pretty usage in npm scripts:"
jq '.scripts | to_entries[] | select(.value | contains("pino-pretty"))' web/package.json
Length of output: 512 Script: #!/bin/bash
# Description: Verify the usage of new logging dependencies in the project
# Test: Search for import or require statements of the new logging libraries
echo "Searching for usage of new logging libraries in .ts and .js files:"
rg -g "*.ts" -g "*.js" -e "from ['\"]@logtail/pino['\"]" -e "from ['\"]pino['\"]" -e "require\(['\"]@logtail/pino['\"]\)" -e "require\(['\"]pino['\"]\)" .
# Test: Check if pino-pretty is used in any npm scripts
echo "Checking for pino-pretty usage in npm scripts:"
jq '.scripts | to_entries[] | select(.value | contains("pino-pretty"))' web/package.json
# Additional Test: Search for pino-pretty usage in configuration files
echo "Searching for pino-pretty usage in configuration files:"
rg -g "*.config.*" "pino-pretty"
Length of output: 886 |
||
"@sentry/react": "^7.55.2", | ||
"@sentry/tracing": "^7.55.2", | ||
"@supabase/supabase-js": "^2.33.1", | ||
|
@@ -87,6 +88,8 @@ | |
"moment": "^2.29.4", | ||
"overlayscrollbars": "^2.3.0", | ||
"overlayscrollbars-react": "^0.5.2", | ||
"pino": "^8.16.0", | ||
"pino-pretty": "^10.2.3", | ||
"react": "^18.2.0", | ||
"react-chartjs-2": "^4.3.1", | ||
"react-dom": "^18.2.0", | ||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,40 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||
import pino, { TransportTargetOptions } from "pino"; | ||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
// Intended for Netlify functions | ||||||||||||||||||||||||||||||||||||||||||||||||
export const createLogger = (logtailToken?: string): pino.Logger => { | ||||||||||||||||||||||||||||||||||||||||||||||||
const targets: TransportTargetOptions[] = [ | ||||||||||||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||||||||||||
target: "pino-pretty", | ||||||||||||||||||||||||||||||||||||||||||||||||
options: {}, | ||||||||||||||||||||||||||||||||||||||||||||||||
level: "info", | ||||||||||||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+7
to
+10
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Consider removing
|
||||||||||||||||||||||||||||||||||||||||||||||||
]; | ||||||||||||||||||||||||||||||||||||||||||||||||
if (logtailToken) { | ||||||||||||||||||||||||||||||||||||||||||||||||
targets.push({ | ||||||||||||||||||||||||||||||||||||||||||||||||
target: "@logtail/pino", | ||||||||||||||||||||||||||||||||||||||||||||||||
options: { sourceToken: logtailToken }, | ||||||||||||||||||||||||||||||||||||||||||||||||
level: "info", | ||||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||
return pino( | ||||||||||||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||||||||||||
level: "info", | ||||||||||||||||||||||||||||||||||||||||||||||||
timestamp: pino.stdTimeFunctions.isoTime, | ||||||||||||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||||||||||||
pino.transport({ targets: targets }) | ||||||||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
export const throwNewError = (logger: pino.Logger, message: string, error?: any) => { | ||||||||||||||||||||||||||||||||||||||||||||||||
if (!error) { | ||||||||||||||||||||||||||||||||||||||||||||||||
logger.error(message); | ||||||||||||||||||||||||||||||||||||||||||||||||
throw new Error(message); | ||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||
if (typeof error === "string") { | ||||||||||||||||||||||||||||||||||||||||||||||||
logger.error(error, message); | ||||||||||||||||||||||||||||||||||||||||||||||||
throw new Error(message + ": " + error); | ||||||||||||||||||||||||||||||||||||||||||||||||
} else if (error instanceof Error) { | ||||||||||||||||||||||||||||||||||||||||||||||||
logger.error(error, message); | ||||||||||||||||||||||||||||||||||||||||||||||||
throw new Error(message + ": " + error.name + ": " + error.message); | ||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+28
to
+40
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Improve error handling in Consider refactoring the function to handle all error types consistently and to preserve stack traces by re-throwing the original error when possible. This ensures that the original error information is not lost, making debugging easier. Apply this diff to improve error handling: -export const throwNewError = (logger: pino.Logger, message: string, error?: any) => {
- if (!error) {
- logger.error(message);
- throw new Error(message);
- }
- if (typeof error === "string") {
- logger.error(error, message);
- throw new Error(message + ": " + error);
- } else if (error instanceof Error) {
- logger.error(error, message);
- throw new Error(message + ": " + error.name + ": " + error.message);
- }
-};
+export const throwNewError = (logger: pino.Logger, message: string, error?: unknown): never => {
+ logger.error({ error }, message);
+ if (error instanceof Error) {
+ throw error;
+ } else if (typeof error === "string") {
+ throw new Error(`${message}: ${error}`);
+ } else {
+ throw new Error(message);
+ }
+}; 📝 Committable suggestion
Suggested change
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ensure
LOGTAIL_TOKEN
is defined and handle missing token casesThe
LOGTAIL_TOKEN
environment variable is used to initialize the logger without a check for its existence. IfLOGTAIL_TOKEN
is undefined,createLogger
may fail or behave unexpectedly. To prevent potential runtime errors, consider adding a check to handle missing or invalidLOGTAIL_TOKEN
values.Apply this diff to add the check:
📝 Committable suggestion