20191023のNode.jsに関する記事は4件です。

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();
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

BigQuery API の Cloud クライアント ライブラリの使用方法

概要

Firebase Functions から Google BigQuery の API を実行するまでに結構てこずったので記録しておこうと思います。ネットで検索するといろいろ情報はあるし公式サイトも充実しているのですが、私が知りたい部分が抜けているような感じがしたので、私が実行できた書き方を紹介致します。

前提

・プロジェクトは作成済
・BigQuery にデータが既に有る

サービスアカウントキー作成

サービスアカウントキーの作成に関しては公式サイトの手順通りに進めれば大丈夫です。
https://cloud.google.com/bigquery/docs/quickstarts/quickstart-client-libraries?hl=ja

BigQuery API を有効化にしていきます
Screen Shot 2019-10-23 at 22.53.10.png
検索するとすぐに出てくるので選択
Screen Shot 2019-10-23 at 22.55.39.png
「有効」をクリック
Screen Shot 2019-10-23 at 22.58.07.png
「認証情報」→「認証情報を作成」→「サービス アカウント」を選択
Screen Shot 2019-10-23 at 22.49.57.png
「サービス アカウント名」を入力し、「作成」をクリック
*ID は自動で作成されます
Screen Shot 2019-10-23 at 23.03.05.png
役割は「オーナー」を選択し、「続行」をクリック
Screen Shot 2019-10-23 at 23.05.54.png
「キーを作成」→「JSON」→「作成」を選択するとダウンロードされます。その後、「完了」をクリックで終了です。
Screen Shot 2019-10-23 at 23.09.56.png

クライアント ライブラリのインストール

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]);
}

projectId はホーム画面で確認できます
Screen Shot 2019-10-23 at 23.24.24.png

以上となります。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

log4js-node の使い方【v5.2.2】

1. はじめに

log4js-nodeは、log4js を javascript 用に移植した log4js を node 用に書き直したものらしい。
Java の log4j とは大きく違うとのこと。
ロガーの使い方がいまいち分かっていなかったので、調査ついでにまとめた。

テスト環境

  • 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.js
import 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)
})

image.png

  • logger.level は ALL (最小) < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < MARK < OFF (最大)
  • default は OFF (console へ出力しない)
  • level は独自のものを設定することも可能だが取り扱わない
  • log4js.shutdown(cb(err)) はログを確実に保存してから終了する際に使用する (flush 処理)
  • 定義されている level は以下を参照

image.png

参考:

ログのカテゴリ (category)

test.js
const 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.')

image.png

  • ログを category ごとにグループ化して出力することができる
  • category は log4js.getLogger([category])で指定する
  • default は default というグループ
  • category ごとに出力する level を指定できる
  • 前項で触れなかったが、複数の引数を使用するとスペース区切りで出力される

参考:

3. 高度な使い方(Configure)

コンソールとファイルに出力 (stdout / file)

test.js
log4js.configure({
  appenders: {
    out: { type: 'stdout' },
    app: { type: 'file', filename: 'application.log' }
  },
  categories: {
    default: { appenders: ['out', 'app'], level: 'debug' }
  }
})

image.png
image.png

  • appender は渡されたログを出力する機械と考える
  • default の category を、 stdout に出力する out と file に出力する app という appender を定義した
  • appender の name(key) は自由に指定可能
  • categories で category ごとに使用する appender と level などを定義できる
  • logger.level は この level と同じ意味となる
  • ファイルに出力されるログは末尾に追記される

参考:

ログのローリング (file / fileSync)

test.js
log4js.configure({
  appenders: {
    app: { type: 'file', filename: 'application.log', maxLogSize: 100, backups: 1 }
  },
  categories: {
    default: { appenders: ['app'], level: 'all' }
  }
})

image.png

  • 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.js
log4js.configure({
  appenders: {
    app: { type: 'dateFile', filename: 'application.log', pattern: '.yyyyMMdd-hhmmss' }
  },
  categories: {
    default: { appenders: ['app'], level: 'all' }
  }
})

image.png

  • 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.js
log4js.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' }
  }
})

image.png
image.png

  • logLevelFilter を用いると appender ごとに出力する level を指定可能
  • 出力する appender にラップするように使用する
  • これによりエラーログだけを別途で処理することも可能
  • ※ appender は上から処理されるので logLevelFilter は最後に配置する必要がある
  • options
    • type: logLevelFilter
    • level: string - 出力する最小の level
    • maxLevel: string - 出力する最大の level 、default は FATAL

参考:

その他

4. 出力ログのレイアウト

標準レイアウト(Built-in Layout)

test.js
log4js.configure({
  appenders: {
    out: { type: 'stdout', layout: { type: 'basic' } }
  },
  categories: {
    default: { appenders: ['out'], level: 'all' }
  }
})
  • 各種 appender に layout を指定することができる
  • 必須ではないので必要に応じて…
  • ※ file 系の appenders に colores を指定しないこと! (色付けは制御文字を使用しているため)

type: basic
image.png

type: coloured / colores
image.png

type: messagePassThrough
image.png

参考:

出力パターン (Pattern format)

test.js
log4js.configure({
  appenders: {
    out: { type: 'stdout', layout: { type: 'pattern', pattern: '%d %[%5p%] %c %m' } }
  },
  categories: {
    default: { appenders: ['out'], level: 'all' }
  }
})

image.png

  • 出力する文字列のパターンを独自に設定できる
  • 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.js
log4js.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')

image.png

  • category で enableCallStack: true を設定すると出力箇所のスタックトレースが取得できる
  • %s の前に %n で改行を入れないと出力が崩れるので注意
  • enableCallStack: false の際、だった各種 field は出力されないが %n は出力される点も注意
  • (スルーできる field があれば便利だったかも)
  • error 出力などの場面で使用するのが良い?

参考:

Tokens

  • key - value の値を埋め込める仕組み
  • token はハードコートせざるを得ないので、どの key を使用しているか定義しておく必要がある
  • これを使うなら message に変数を結合させるのがベストのような?
  • 発行ユーザーなどの用途なら悪くはなさそう
  • ここでは取り扱わない

参考:

カスタムレイアウト (Custom Layout)

test.js
log4js.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')

image.png

  • 独自のレイアウトを定義することも可能
  • appender で指定した設定が config から取得できる
  • functionName 以下は enableCallStack: true が必要
  • callback の引数は logEvent の Object なので、これを加工して return に返す

参考:

5. サンプル

test.js
import 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(() => {})

image.png
image.png

  • console のカラーリングと、2つのファイルに書き出す形式
    • console への出力は LOG_LEVEL で制御する
  • ファイルは INFO 以上と WARN 以上の二種類を用意 (error を把握しやすくする)
    • これにローリングを加えれば完成となる (見やすさのため省略)
    • terminal が 256色 をサポートしていない場合、通常色出力となる
  • 独自の level を定義することは可能だが、カラーリングはサポートしないらしい?

参考:

以上

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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_modulesdist ファイルは`同じプロジェクト内にローカル環境だけは共存している形になると思います。
ローカルで以下のようなdockerfileをもとにvue.js のプロジェクトをbuildしたのですが、

Dockerfile
COPY ./vue ./
RUN npm install
RUN npm run build

このときの COPY の挙動ではローカルに含まれるnode_modulesdist ファイルも一緒にコンテナ内に配置されてしまいます。
そのため、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とかは記事とかそこそこあるんですけどね…

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む