用于记录错误的适当代码设计
Appropriate code design for logging errors
我正在编写一个函数,它将通过 Google Cloud Functions 作为 API 公开。它获取用户坐标,通过 API 查找位置名称,然后使用该信息调用另一个 API,然后 returns 该数据。
我的问题是关于如何最好地处理错误。到目前为止,我已经实现了类似于下面代码中描述的东西,但我不确定它是好的还是 "right"...
// index.js
const { getCityByLatLon } = require('./location.js');
const { getWeatherByCity } = require('./weather.js');
module.exports.getWeather = async (req, res) => {
try {
// Validate request - implementation not important
} catch (e) {
// Don't care about logging bad input anywhere, just send error to consumer
return res.status(422).json({ error: e.message });
}
const { lat, lon } = req.query;
try {
const city = await getCityByLatLon(lat, lon);
const weather = await getWeatherByCity(city);
res.json(weather);
} catch (e) {
// Need to return an error here...
// Should it be the message that either getCityByLatLon or getWeatherByCity
// gives, or should it be handled a different way?
res.status(500).json({ error: e.message });
}
};
// location.js
module.exports.getCityByLatLon = (lat, lon) => {
const mapsClient = new Client(); // Implementation not important
try {
const result = await mapsClient.reverseGeocode(lat, lon);
if (result.status !== 'OK') {
// We can't handle this, but should log it and tell the caller
// that something went wrong
throw new Error(result.error_message);
}
if (result.data.length < 1) {
throw new Error('No results');
}
return result.data[0].city;
} catch (e) {
// I think I want to log this exception and store it somewhere
logger.log(e);
// And throw a "nice" error which doesn't give info on the underlying
// error, for the API response to return
throw new Error('Failed to get City');
}
};
// weather.js
module.exports.getWeatherByCity = (city) => {
const weatherClient = new Client(); // Implementation not important
try {
const result = await weatherClient.fetchNextWeek(city);
if (result.status !== 'OK') {
// We can't handle this, but should log it and tell the caller
// that something went wrong
throw new Error(result.error_message);
}
return result.data;
} catch (e) {
// I think I want to log this exception and store it somewhere
logger.log(e);
// And throw a "nice" error which doesn't give info on the underlying
// error, for the API response to return
throw new Error('Failed to get Weather');
}
};
这真的是一个好习惯吗?我认为可能是,因为 getCityByLatLon
和 getWeatherByCity
不会暴露特定于实现的错误。但是,WebStorm 让我重新思考,通过显示检查 ('throw' of exception caught locally
) 因为我 throw
ing 在 try
块内,然后立即处理它。
常见的方法应该是将错误传递给链中最顶层的函数,这有助于干净的代码实现并避免代码中出现过多的 try/catch
。在您的情况下,调用者有责任记录消息,因为函数预计会失败。为每个函数添加一个日志语句并不理想,因为它可能会导致在您的日志系统中出现重复的错误消息。
考虑调用者也在 catch 块中打印错误的情况,在这种情况下,错误会在您的控制台中打印两次。
常见的做法是抛出错误,让调用者决定如何处理错误。
一种更常见和更可靠的方法是向请求路由器添加一个错误中间件来处理系统中的错误日志记录。您可以将错误中间件附加到您的快速请求路由器,如下所示:
import { Router } from "express";
const router = Router();
// ... your routes in the format router("/").get()
router.use((err, req, res, next) => {
// we have got an error, we can log it
console.log(err);
res.status(500).json({ error: e.message });
});
现在,在你的控制器中,你可以使用这样的东西
module.exports.getWeather = async (req, res, next) => {
try {
// Validate request - implementation not important
} catch (e) {
// Don't care about logging bad input anywhere, just send error to
consumer
return res.status(422).json({ error: e.message });
}
const { lat, lon } = req.query;
try {
const city = await getCityByLatLon(lat, lon);
const weather = await getWeatherByCity(city);
res.json(weather);
} catch (e) {
// Need to return an error here...
// Should it be the message that either getCityByLatLon or
getWeatherByCity
// gives, or should it be handled a different way?
return next(err); // this forces your design to use only one type of response payload for all kind of 500 errors. The schema of your object remains the same. Also, your errors will get logged as well.
}
};
我正在编写一个函数,它将通过 Google Cloud Functions 作为 API 公开。它获取用户坐标,通过 API 查找位置名称,然后使用该信息调用另一个 API,然后 returns 该数据。
我的问题是关于如何最好地处理错误。到目前为止,我已经实现了类似于下面代码中描述的东西,但我不确定它是好的还是 "right"...
// index.js
const { getCityByLatLon } = require('./location.js');
const { getWeatherByCity } = require('./weather.js');
module.exports.getWeather = async (req, res) => {
try {
// Validate request - implementation not important
} catch (e) {
// Don't care about logging bad input anywhere, just send error to consumer
return res.status(422).json({ error: e.message });
}
const { lat, lon } = req.query;
try {
const city = await getCityByLatLon(lat, lon);
const weather = await getWeatherByCity(city);
res.json(weather);
} catch (e) {
// Need to return an error here...
// Should it be the message that either getCityByLatLon or getWeatherByCity
// gives, or should it be handled a different way?
res.status(500).json({ error: e.message });
}
};
// location.js
module.exports.getCityByLatLon = (lat, lon) => {
const mapsClient = new Client(); // Implementation not important
try {
const result = await mapsClient.reverseGeocode(lat, lon);
if (result.status !== 'OK') {
// We can't handle this, but should log it and tell the caller
// that something went wrong
throw new Error(result.error_message);
}
if (result.data.length < 1) {
throw new Error('No results');
}
return result.data[0].city;
} catch (e) {
// I think I want to log this exception and store it somewhere
logger.log(e);
// And throw a "nice" error which doesn't give info on the underlying
// error, for the API response to return
throw new Error('Failed to get City');
}
};
// weather.js
module.exports.getWeatherByCity = (city) => {
const weatherClient = new Client(); // Implementation not important
try {
const result = await weatherClient.fetchNextWeek(city);
if (result.status !== 'OK') {
// We can't handle this, but should log it and tell the caller
// that something went wrong
throw new Error(result.error_message);
}
return result.data;
} catch (e) {
// I think I want to log this exception and store it somewhere
logger.log(e);
// And throw a "nice" error which doesn't give info on the underlying
// error, for the API response to return
throw new Error('Failed to get Weather');
}
};
这真的是一个好习惯吗?我认为可能是,因为 getCityByLatLon
和 getWeatherByCity
不会暴露特定于实现的错误。但是,WebStorm 让我重新思考,通过显示检查 ('throw' of exception caught locally
) 因为我 throw
ing 在 try
块内,然后立即处理它。
常见的方法应该是将错误传递给链中最顶层的函数,这有助于干净的代码实现并避免代码中出现过多的 try/catch
。在您的情况下,调用者有责任记录消息,因为函数预计会失败。为每个函数添加一个日志语句并不理想,因为它可能会导致在您的日志系统中出现重复的错误消息。
考虑调用者也在 catch 块中打印错误的情况,在这种情况下,错误会在您的控制台中打印两次。
常见的做法是抛出错误,让调用者决定如何处理错误。
一种更常见和更可靠的方法是向请求路由器添加一个错误中间件来处理系统中的错误日志记录。您可以将错误中间件附加到您的快速请求路由器,如下所示:
import { Router } from "express";
const router = Router();
// ... your routes in the format router("/").get()
router.use((err, req, res, next) => {
// we have got an error, we can log it
console.log(err);
res.status(500).json({ error: e.message });
});
现在,在你的控制器中,你可以使用这样的东西
module.exports.getWeather = async (req, res, next) => {
try {
// Validate request - implementation not important
} catch (e) {
// Don't care about logging bad input anywhere, just send error to
consumer
return res.status(422).json({ error: e.message });
}
const { lat, lon } = req.query;
try {
const city = await getCityByLatLon(lat, lon);
const weather = await getWeatherByCity(city);
res.json(weather);
} catch (e) {
// Need to return an error here...
// Should it be the message that either getCityByLatLon or
getWeatherByCity
// gives, or should it be handled a different way?
return next(err); // this forces your design to use only one type of response payload for all kind of 500 errors. The schema of your object remains the same. Also, your errors will get logged as well.
}
};