はじめに
GoogleAppsScript(GAS)でconsoleのスプレッドシートシート出力機能を作成しました。GASではLogger
やconsole
などのログ表示機能が備わっておりますが、ここでは使用しません。コピペですぐに使えるものを目指しています。
機能としては下記の通りです。
- ログレベル
- 出力先指定
使い方
使い方は下記の通りです。
main.gs
function main() {
try {
console.error('log: error.');
console.warn('log: warn.');
console.info('log: info.');
console.debug('log: debug.');
console.log({a:"aaa",b:1,c:{d:2},d:[3,4,5]});
throw new Error('log: throw error');
} catch (e) {
console.error(e.stack);
}
}
スプレッドシートにログが下記のように出力されます。
timestamp | level | path | message |
---|---|---|---|
2020-05-24 21:59:53:916 | ERROR | src/main(3) | log: error. |
2020-05-24 21:59:54:203 | WARN | src/main(4) | log: warn. |
2020-05-24 21:59:54:540 | INFO | src/main(5) | log: info. |
2020-05-24 21:59:54:938 | DEBUG | src/main(6) | log: debug. |
2020-05-24 21:59:55:207 | DEBUG | src/main(7) | {a:"aaa",b:1,c:{d:2},d:[3,4,5]} |
2020-05-24 21:59:55:452 | ERROR | src/main(10) | Error: log: throw error at main (src/main:8:15) |
ログファイル出力の実装
- GoogleDriveで「Google スプレッドシート」を新規作成する。スプレッドシードのURL
https://docs.google.com/spreadsheets/d/*****/edit?
の*****
の部分がIDになる。 - GoogleDriveで「Google Apps Script」を新規作成する。*.gsファイルに以下のJavaScriptコードを実装する。
JavaScriptの実装
log.gs
let log = {}; {
const config = {
SSID_LOG: '*****', // ※ログ用のスプレッドシートのIDを指定する
SSN_LOG: '*****', // ※ログ用のスプレッドシートのシート名を指定する
IS_LOGFILE: true, // ログフラグ true=ログファイル出力あり/false=なし
LOG_LEVEL: 3, // ログレベル 0=ERROR/1=WARN/2=INFO/3=DEBUG
};
const logLevel = { // ログレベル
ERROR: 0,
WARN: 1,
INFO: 2,
DEBUG: 3,
};
let spreadsheet = SpreadsheetApp.openById(config.SSID_LOG);
let sheet = spreadsheet.getSheetByName(config.SSN_LOG);
/**
* 初期化
*/
log.init = () => {
if (config.IS_LOGFILE) {
overrideConsole();
}
}
/**
* console関数を上書きする
*/
let overrideConsole = () => {
console = {};
console.error = (...args) => {
if (logLevel.ERROR <= config.LOG_LEVEL) {
out('ERROR', args);
}
}
console.warn = (...args) => {
if (logLevel.WARN <= config.LOG_LEVEL) {
out('WARN', args);
}
}
console.info = (...args) => {
if (logLevel.INFO <= config.LOG_LEVEL) {
out('INFO', args);
}
}
console.debug = (...args) => {
if (logLevel.DEBUG <= config.LOG_LEVEL) {
out('DEBUG', args);
}
}
console.log = (...args) => {
if (logLevel.DEBUG <= config.LOG_LEVEL) {
out('DEBUG', args);
}
}
console.ws = null;
}
/**
* ログを出力する
* @param {String} level ログレベル
* @param {String} msg メッセージ
*/
let out = (level, msg) => {
let callerInfo = {};
let tmpPrepareST = Error.prepareStackTrace;
Error.prepareStackTrace = (e, stack) => {
let caller = stack[1];
return {
file: caller.getFileName(),
line: caller.getLineNumber(),
}
};
Error.captureStackTrace(callerInfo, out);
let file = callerInfo.stack.file;
let line = callerInfo.stack.line;
Error.prepareStackTrace = tmpPrepareST;
let timestamp = Utilities.formatDate(new Date(), 'Asia/Tokyo', 'yyyy-MM-dd HH:mm:ss:SSS');
let message = convertMsg(msg);
sheet.appendRow([timestamp, level, `${file}(${line})`, message]);
}
/**
* ログメッセージを変換する
* @param {Array} arg メッセージ
*/
let convertMsg = (arg) => {
let msg = '';
for (let i = 0; i < arg.length; i++) {
msg += ' ' + dumpObject(arg[i]);
}
return msg;
}
/**
* オブジェクトを出力する
* @param {Object} obj
*/
let dumpObject = (obj) => {
let v = '';
let t = typeof obj;
switch (t) {
case 'number':
case 'boolean':
v = obj;
break;
case 'string':
v = '"' + obj + '"';
break;
case 'object':
if (isArray(obj)) {
v = '[';
let count = 0;
for (let value of obj) {
v += dumpObject(value);
count++;
if (count < obj.length) {
v += ',';
}
}
v += ']';
} else if (isObject(obj) || isError(obj)) {
v = '{';
let count = 0;
let nameList = Object.getOwnPropertyNames(obj);
for (let key of nameList) {
let ret = dumpObject(obj[key]);
if (ret) {
v += key + ':' + ret;
count++;
if (count < nameList.length) {
v += ',';
}
}
}
v += '}';
} else if (isString(obj) || isDate(obj)) {
v = '"' + obj.toString() + '"';
} else if (isNumber(obj) || isBoolean(obj)) {
v = obj.valueOf();
} else if (isFunction(obj)) {
v = '<fuction>';
} else if (isNull(obj)) {
v = '<null>';
} else if (isUndefined(obj)) {
v = '<undefined>';
}
break;
case 'function':
v = '<function>';
break;
case 'symbol':
v = '<symbol>';
break;
case 'undefined':
v = '<undefined>';
break;
}
return v;
}
let isArray = (obj) => {
return Object.prototype.toString.call(obj) === '[object Array]';
}
let isBoolean = (obj) => {
return Object.prototype.toString.call(obj) === '[object Boolean]';
}
let isDate = (obj) => {
return Object.prototype.toString.call(obj) === '[object Date]';
}
let isError = (obj) => {
return Object.prototype.toString.call(obj) === '[object Error]';
}
let isNumber = (obj) => {
return Object.prototype.toString.call(obj) === '[object Number]';
}
let isObject = (obj) => {
return Object.prototype.toString.call(obj) === '[object Object]';
}
let isString = (obj) => {
return Object.prototype.toString.call(obj) === '[object String]';
}
let isFunction = (obj) => {
return Object.prototype.toString.call(obj) === '[object Function]';
}
let isNull = (obj) => {
return Object.prototype.toString.call(obj) === '[object Null]';
}
let isUndefined = (obj) => {
return Object.prototype.toString.call(obj) === '[object Undefined]';
}
}
log.init();
参考リンク
さいごに
ソースコードをGitHubに公開しています。
以上です。