- 投稿日:2019-10-23T23:53:50+09:00
Cloud FunctionsからFirestoreの24時間以内のデータだけを取得する
これで取れました。
// 現在の時間から24時間前を取得 var agoDate = new Date(); agoDate.setHours(agoDate.getHours() - 24); const timelineDataSnap: FirebaseFirestore.QuerySnapshot = await db .collection("timeline") .doc(doc.id) .collection("images") .where("created_at", ">", admin.firestore.Timestamp.fromDate(agoDate)) .get();
- 投稿日:2019-10-23T23:31:02+09:00
BigQuery API の Cloud クライアント ライブラリの使用方法
概要
Firebase Functions から Google BigQuery の API を実行するまでに結構てこずったので記録しておこうと思います。ネットで検索するといろいろ情報はあるし公式サイトも充実しているのですが、私が知りたい部分が抜けているような感じがしたので、私が実行できた書き方を紹介致します。
前提
・プロジェクトは作成済
・BigQuery にデータが既に有るサービスアカウントキー作成
サービスアカウントキーの作成に関しては公式サイトの手順通りに進めれば大丈夫です。
https://cloud.google.com/bigquery/docs/quickstarts/quickstart-client-libraries?hl=jaBigQuery API を有効化にしていきます
検索するとすぐに出てくるので選択
「有効」をクリック
「認証情報」→「認証情報を作成」→「サービス アカウント」を選択
「サービス アカウント名」を入力し、「作成」をクリック
*ID は自動で作成されます
役割は「オーナー」を選択し、「続行」をクリック
「キーを作成」→「JSON」→「作成」を選択するとダウンロードされます。その後、「完了」をクリックで終了です。
クライアント ライブラリのインストール
npm install --save @google-cloud/bigqueryソースコード
*このソースは Firebase Functions から呼び出す想定の書き方となっています
const { BigQuery } = require("@google-cloud/bigquery"); export async function index() { const bigQuery = new BigQuery({ projectId: "xxxx", credentials: require("[path]/xxxx.json") // ダウンロードした JSON ファイル }); // クエリ const sqlQuery = ` SELECT xxxxx FROM \`xxxxx\` WHERE xxxx = xxxx `; const options = { query: sqlQuery, location: "US" // BigQuery の location }; const [rows] = await bigQuery.query(options); console.log([rows]); }以上となります。
- 投稿日:2019-10-23T21:34:29+09:00
log4js-node の使い方【v5.2.2】
1. はじめに
log4js-nodeは、log4js を javascript 用に移植した log4js を node 用に書き直したものらしい。
Java の log4j とは大きく違うとのこと。
ロガーの使い方がいまいち分かっていなかったので、調査ついでにまとめた。
- log4js-node/log4js-node: A port of log4js to node.js
- Official-Docs: log4js-node by log4js-node
- GitHub-Docs: log4js-node by log4js-node
テスト環境
- Windows10, MINGW64, VSCode
- node: 10.14.2
- babel-node: 7.6.1
- log4js: 5.2.2
- ES6 形式で書きたかったので
babel-nodeを使用- ログファイルは VSCode + Log File Highlighter で表示 (ちゃんと設定してないです…)
console$ npm install log4js参考:
2. 基本的な使い方
ログの出力(logging)
test.jsimport log4js from 'log4js' const logger = log4js.getLogger() logger.level = 'all' logger.trace('Some trace messages') logger.debug('Some debug messages') logger.info('Some info messages') logger.warn('Some warn messages') logger.error('Some error messages') logger.fatal('Some fatal messages') log4js.shutdown((err) => { if (err) throw err process.exit(0) })
logger.levelは ALL (最小) < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < MARK < OFF (最大)- default は
OFF(console へ出力しない)- level は独自のものを設定することも可能だが取り扱わない
log4js.shutdown(cb(err))はログを確実に保存してから終了する際に使用する (flush 処理)- 定義されている level は以下を参照
参考:
ログのカテゴリ (category)
test.jsconst logger = log4js.getLogger() logger.level = 'all' logger.info('Some info messages', 'append', 'more') const cheseLogger = log4js.getLogger('cheese') cheseLogger.level = 'all' cheseLogger.info('Cheese is Comte.')
- ログを category ごとにグループ化して出力することができる
- category は
log4js.getLogger([category])で指定する- default は
defaultというグループ- category ごとに出力する level を指定できる
- 前項で触れなかったが、複数の引数を使用するとスペース区切りで出力される
参考:
3. 高度な使い方(Configure)
コンソールとファイルに出力 (stdout / file)
test.jslog4js.configure({ appenders: { out: { type: 'stdout' }, app: { type: 'file', filename: 'application.log' } }, categories: { default: { appenders: ['out', 'app'], level: 'debug' } } })
- appender は渡されたログを出力する機械と考える
defaultの category を、 stdout に出力するoutと file に出力するappという appender を定義した- appender の
name(key)は自由に指定可能- categories で category ごとに使用する appender と level などを定義できる
logger.levelは この level と同じ意味となる- ファイルに出力されるログは末尾に追記される
参考:
ログのローリング (file / fileSync)
test.jslog4js.configure({ appenders: { app: { type: 'file', filename: 'application.log', maxLogSize: 100, backups: 1 } }, categories: { default: { appenders: ['app'], level: 'all' } } })
- file 系の appender は書き込むファイルをローテートすることが可能
- 上記は
application.log.2が保持上限を超えて削除されているtype: fileSyncで同期的に書き込むことも可能 (test などの用途)- options
- type:
file/fileSync- filename: string - ログファイルの名称 (xxx.ext)
- maxLogSize: integer - 1ファイルの最大サイズ (byte)、default は上限なし
- backups: integer - 保持するファイル数、default は
5- compress: boolean -
trueで ローリングしたファイルを圧縮する(.gz)- keepFileExt: boolean -
trueで 拡張子を保持する (xxx.ext.1 を xxx.1.ext にする)- encoding: string - default は
utf-8参考:
日付ごとのローリング(dateFile)
test.jslog4js.configure({ appenders: { app: { type: 'dateFile', filename: 'application.log', pattern: '.yyyyMMdd-hhmmss' } }, categories: { default: { appenders: ['app'], level: 'all' } } })
patternで指定した日付の粒度でローテートすることが可能- 上記は1秒ごとに切り替える設定で二回実行した結果
- file 系と違って保持数は指定できない?(要検証)
- 保持日数は
daysToKeppで指定できる- option
- type:
dateFile- filename: string - ログファイルの名称 (xxx.ext)
- pattern: string - ローテートするタイミング、default は
.YYYY-MM-dd、参照:date-format- daysToKeep: integer - 保持する日数、 default は上限なし
- alwaysIncludePattern: boolean -
trueで現在のログファイル名にも日付を付与する- compress: boolean -
trueで ローリングしたファイルを圧縮する(.gz)- keepFileExt: boolean -
trueで 拡張子を保持する (xxx.ext.date を xxx.date.ext にする)- encoding: string - default は
utf-8参考:
appendars のレベルフィルター (logLevelFilter)
test.jslog4js.configure({ appenders: { out: { type: 'stdout' }, app: { type: 'file', filename: 'application.log' }, wrapErr: { type: 'logLevelFilter', appender: 'app', level: 'warn' } }, categories: { default: { appenders: ['out', 'wrapErr'], level: 'all' } } })
logLevelFilterを用いると appender ごとに出力する level を指定可能- 出力する appender にラップするように使用する
- これによりエラーログだけを別途で処理することも可能
- ※ appender は上から処理されるので logLevelFilter は最後に配置する必要がある
- options
- type:
logLevelFilter- level: string - 出力する最小の level
- maxLevel: string - 出力する最大の level 、default は
FATAL参考:
その他
- 他にも SMTP や Slack に出力するものなどがある
- log4js-node by log4js-node
4. 出力ログのレイアウト
標準レイアウト(Built-in Layout)
test.jslog4js.configure({ appenders: { out: { type: 'stdout', layout: { type: 'basic' } } }, categories: { default: { appenders: ['out'], level: 'all' } } })
- 各種 appender に layout を指定することができる
- 必須ではないので必要に応じて…
- ※ file 系の appenders に
coloresを指定しないこと! (色付けは制御文字を使用しているため)参考:
出力パターン (Pattern format)
test.jslog4js.configure({ appenders: { out: { type: 'stdout', layout: { type: 'pattern', pattern: '%d %[%5p%] %c %m' } } }, categories: { default: { appenders: ['out'], level: 'all' } } })
- 出力する文字列のパターンを独自に設定できる
- field は
%[padding].[truncation][field]{[format]}で定義されている
- padding: integer - 空白埋め文字数、右詰め
- truncation: integer - 最大文字数、切り捨て
- field: string - フィールド名
- format: string(option) - フィールドのオプション (任意)
- 上記の場合、「日付 level(5文字埋め)を色付きで カテゴリ 本文」となる
- ※ file 系の appenders に
%[%]を指定しないこと! (色付けは制御文字を使用しているため)- pattern field (サンプル)
- %r: ローカル時刻 (02:19:11)
- %p: ログ level (INFO)
- %c: ログ category (default)
- %h: ホスト名 (PCの名前等)
- %m: メッセージ
- %d: 日時、{ISO8601 |
yyyy-MM-ddThh:mm:ss.SSS} (2019-10-23T02:13:53.028)、参照:date-format- %%: %のエスケープ
- %n: 改行
- %z: プロセスID、process.pid (28076)
- %[: 色付け開始
- %]: 色付け終了
enableCallStack: true時の field (後述)
- %f: ログ出力元のファイル名、{depth |
0} (C:\xxx\test.js)- %l: ログ出力元の行番号 (13)
- %o: ログ出力元の列位置 (8)
- %s: スタックトレース ( at Object.info (C:\xxx/test.js:13:8) \n ...)
- トークンは取り扱わない
参考:
enableCallStack
test.jslog4js.configure({ appenders: { out: { type: 'stdout', layout: { type: 'pattern', pattern: '%[[%d] %p %c -%] %m%n%f %l %o%n%s' } } }, categories: { default: { appenders: ['out'], level: 'all', enableCallStack: true } } }) const logger = log4js.getLogger() logger.info('Some info messages')
- category で
enableCallStack: trueを設定すると出力箇所のスタックトレースが取得できる%sの前に%nで改行を入れないと出力が崩れるので注意enableCallStack: falseの際、だった各種 field は出力されないが%nは出力される点も注意- (スルーできる field があれば便利だったかも)
- error 出力などの場面で使用するのが良い?
参考:
Tokens
- key - value の値を埋め込める仕組み
- token はハードコートせざるを得ないので、どの key を使用しているか定義しておく必要がある
- これを使うなら message に変数を結合させるのがベストのような?
- 発行ユーザーなどの用途なら悪くはなさそう
- ここでは取り扱わない
参考:
カスタムレイアウト (Custom Layout)
test.jslog4js.addLayout('json', function(config) { return function(logEvent) { return JSON.stringify(logEvent, null, 2) + config.separator } }) log4js.configure({ appenders: { out: { type: 'stdout', layout: { type: 'json', separator: ',' } } }, categories: { default: { appenders: ['out'], level: 'all', enableCallStack: true } } }) const logger = log4js.getLogger() logger.info('Some info messages')
- 独自のレイアウトを定義することも可能
- appender で指定した設定が config から取得できる
functionName以下はenableCallStack: trueが必要- callback の引数は
logEventの Object なので、これを加工して return に返す参考:
5. サンプル
test.jsimport os from 'os' import log4js from 'log4js' import dateFormat from 'date-format' import chalk from 'chalk' const LOG_LEVEL = 'ALL' // DEBUG: TRACE or DEBUG, PRODUCTION: INFO or OFF const levelColors = { TRACE: { meta: 'grey', body: 'grey', trace: null }, DEBUG: { meta: 'green', body: 'grey', trace: null }, INFO: { meta: 'cyan', body: 'white', trace: null }, WARN: { meta: 'yellow', body: 'yellow', trace: null }, ERROR: { meta: 'red', body: 'red', trace: 'white' }, FATAL: { meta: 'magenta', body: 'magenta', trace: 'white' } } const coloring = function(color, text) { if (color) { return chalk[color](text) } return text } log4js.addLayout('origin', function({ addColor }) { return function(e) { const date = new Date(e.startTime) const level = e.level.levelStr.toUpperCase() // 大文字 const hasCallStack = e.hasOwnProperty('callStack') // callStack を持っているか const dateStr = dateFormat('yyyy-MM-dd hh:mm:ss.SSS', date) const message = e.data.join(' ') // データはスペース区切り const levelStr = level.padEnd(5).slice(0, 5) // 5文字 const color = levelColors[level] // メタ情報 const meta = `${levelStr} ${dateStr} [${e.categoryName}]` const prefix = addColor ? coloring(color.meta, meta) : meta // ログ本体 const body = addColor ? coloring(color.body, message) : message // スタックトレース let suffix = '' if (hasCallStack && color.trace) { const callStack = e.callStack suffix += os.EOL suffix += addColor ? coloring(color.trace, callStack) : callStack } return `${prefix} ${body}${suffix}` } }) log4js.configure({ appenders: { out: { type: 'stdout', layout: { type: 'origin', addColor: true } }, logFile: { type: 'file', filename: 'logs/application.log', layout: { type: 'origin', addColor: false } }, errFile: { type: 'file', filename: 'logs/error.log', layout: { type: 'origin', addColor: false } }, log: { type: 'logLevelFilter', appender: 'logFile', level: 'info' }, err: { type: 'logLevelFilter', appender: 'errFile', level: 'warn' }, }, categories: { default: { appenders: ['out', 'log', 'err'], level: LOG_LEVEL, enableCallStack: true } } }) const logger = log4js.getLogger() logger.trace('Some trace messages') logger.debug('Some debug messages') logger.info('Some info messages') logger.warn('Some warn messages') logger.error('Some error messages') logger.fatal('Some fatal messages') log4js.shutdown(() => {})
- console のカラーリングと、2つのファイルに書き出す形式
- console への出力は
LOG_LEVELで制御する- ファイルは
INFO以上とWARN以上の二種類を用意 (error を把握しやすくする)
- これにローリングを加えれば完成となる (見やすさのため省略)
- terminal が 256色 をサポートしていない場合、通常色出力となる
- 独自の level を定義することは可能だが、カラーリングはサポートしないらしい?
参考:
以上
- 投稿日:2019-10-23T12:22:24+09:00
dockerを使って構築した、vue.jsのアプリがデプロイした際に動かなくなった話
dockerのマルチステージビルドを使って構築した、vue.jsのアプリがデプロイした際にローカル環境では発生しないエラーが実際のサーバー上でだけ発生する事象が発生しました。
通常通りのやり方だと気づけなかったポイントなので、共有しようと思います。マルチステージビルドとは?
マルチステージビルドはdockerのimageを作成する際、
docker build時にだけ立ち上がるコンテナでそこでできた生成物を実際のイメージに配置するみたいなことができます。
どこかの環境で(jenkins等)node.jsをbuildしてその生成物を配置するみたいな必要がなくなりdockerだけで完結できるようになるのでVue.js の公式も推奨しているデプロイ方法になります。ハマったポイント
ローカルで
npm installしたときに生成されるnode_modulesと サーバーのコンテナ上で生成されるnode_modulesに差分があり、ローカル環境でうまく実行できていたものがサーバーのコンテナ上ではうまく実行されずエラーになってしまった。原因
ローカル環境で
Vue.jsの開発をするときは、npm run dev等で ローカルのサーバーを立てて開発することが多いと思います。
そのため、node_modulesやdistファイルは`同じプロジェクト内にローカル環境だけは共存している形になると思います。
ローカルで以下のようなdockerfileをもとにvue.js のプロジェクトをbuildしたのですが、DockerfileCOPY ./vue ./ RUN npm install RUN npm run buildこのときの
COPYの挙動ではローカルに含まれるnode_modulesやdistファイルも一緒にコンテナ内に配置されてしまいます。
そのため、RUN npm installが実行されても、新規でnode_modulesが生成されることがなく baseimageのversionとローカルのversionの差分等があるとうまく生成されないというような事象が発生してしまっていました。対処方法
.dockerignoreと呼ばれるdockerから無視させるファイルリストを追加する方法があるので、このファイルに以下のように無視ファイルの対象として追加する。
./vue/dist/ ./vue/node_modules/※ディレクトリ階層は使っているものに合わせてください。
追加することで、
docker build時は毎回新規でnpm installが走るようになるので、サーバーだろうがローカル環境だろうが同じ状態にすることができ、問題を解決することができました。さいごに
今回のミスは、たまたまアプリを起動するときに出てくるエラーで動作の一部ができなくなるという感じのエラーではなく気づくことができましたが、ほんとに一部でしか使っていない動作でのエラーとかだとリリースしてからエラーが発生する等の問題になりかねないので注意が必要ですね。
あと、今回差分が発生してしまった一番の大きな原因は dockerの
baseimageのversionがnode:6.4とかだったのに対して、ローカル環境が10くらいになっていたことだったので、プロジェクトの生成からdockerのコンテナ内でやったほうが幸せになれると思いました。 Rubyとかは記事とかそこそこあるんですけどね…
























