GASで擬似的なアクセスログをつくる

ある日、情シスより突然放たれた非情な宣告。

「以後アクセスログは渡せません。」

理由は明かされなかったが、そんなことどうでもいい。
なにせ、フロントエンドしか扱えない環境で、しかもこの業務はアクセスログがなければ成立しないのだ。

業務存続危機である。
ヤバし。

そこそこ考えた末、GoogleスプレッドシートとGAS（Google App Script）を使って擬似的なアクセスログをつくることにした。

## 概要
ログに記録したい情報をGoogleスプレッドシートにPOST送信し、GASでその情報を記録する。

フロントエンドだけでも、JavaScript（windowオブジェクトやscreenオブジェクトなど）を使って様々な情報を取得することができる。

なお、後述するが、この方法で記録する場合は、Webで公開可能な情報しか扱えないことに留意されたい。

## 事前準備
予め受け側のスプレッドシートを作成し、Web公開しておく。
なお、スプレッドシート名は任意の名前で（今回は使用しない）、ワークシート名は「AllLog」とする。

スプレッドシートを作成したら、スプレッドシートのkeyとworksheetIdを確認しておく。
※スプレッドシートのkeyとworksheetIdの確認方法は下記を参照
[Googleスプレッドシートをjsから読み書きしてみた - derax’s blog](http://derax.hateblo.jp/entry/2015/09/25/170829)

## コード
### クライアント側
```writeLog.js
//*************************************
// 記録したい情報をpostdataに「変数名&変数&...」と格納し、それをスプレッドシートのkey(gasurl)にPOST送信する
//*************************************
function writeLog() {
  var gasurl = "予め確認しておいたkey";
  var lHr = window.location.href;
  var lPa = lHr.match(".+/(.+?)([?#;].*)?$")[1];
  var lSe = window.location.search;
  lHr = encodeURIComponent(lHr); //URLをpost先で取り込む際、'&'があるとそこまでと判定されるためURLエンコードしておく（GAS側でデコードする）
  lSe = encodeURIComponent(lSe);//URLをpost先で取り込む際、'&'があるとそこまでと判定されるためURLエンコードしておく（GAS側でデコードする）
  var lHa = window.location.hash;
  var dTi = encodeURIComponent(window.document.title);
  var dRe = myGetQuery("referrer");
  if (dRe === null) {
    dRe = window.document.referrer;
  }
  var nAc = navigator.appCodeName;
  var nAn = navigator.appName;
  var nAv = navigator.appVersion;
  var nCe = navigator.cookieEnabled;
  var nPl = navigator.platform;
  var nUa = navigator.userAgent;
  var nCp = navigator.cpuClass;
  // 最優先の言語だけ取得
  var uLa =
    (window.navigator.languages && window.navigator.languages[0]) ||
    window.navigator.language ||
    window.navigator.userLanguage ||
    window.navigator.browserLanguage;
  // クライアント側で受け付けている言語リストを取得
  var cLa = window.navigator.languages || [
    window.navigator.language ||
    window.navigator.userLanguage ||
    window.navigator.browserLanguage
  ];
  var sWi = screen.width;
  var sHe = screen.height;
  var wAv = screen.availWidth;
  var sAl = screen.availHeight;
  var sCl = screen.colorDepth;
  var sPi = screen.pixelDepth;
  var postdata =
    "lHr=" +
    lHr +
    "&lPa=" +
    lPa +
    "&lSe=" +
    lSe +
    "&lHa=" +
    lHa +
    "&dTi=" +
    dTi +
    "&dRe=" +
    dRe +
    "&dev=" +
    dev +
    "&nAc=" +
    nAc +
    "&nAn=" +
    nAn +
    "&nAv=" +
    nAv +
    "&nCe=" +
    nCe +
    "&nPl=" +
    nPl +
    "&nUa=" +
    nUa +
    "&nCp=" +
    nCp +
    "&uLa=" +
    uLa +
    "&cLa=" +
    cLa +
    "&sWi=" +
    sWi +
    "&sHe=" +
    sHe +
    "&wAv=" +
    wAv +
    "&sAl=" +
    sAl +
    "&sCl=" +
    sCl +
    "&sPi=" +
    sPi;
  $.ajax({
    url: gasurl,
    type: "POST",
    data: postdata,
    dataType: "text"
  });
}
```
### GoogleAppScript側
```doPost.js
//*************************************
// POST送信された情報(e)の各パラメータを各変数に格納し、sheet.appendRowでスプレッドシートの最下行に挿入する
//*************************************
function doPost(e) {
  var spreadsheetId = "予め確認しておいたworksheetId";
  var ss = SpreadsheetApp.openById(spreadsheetId);
  var sheet;
  sheet = ss.getSheetByName("AllLog"); //シート名
  var lHr = e.parameter.lHr; //location.href
  lHr = decodeURIComponent(lHr); //POSTの際エンコードされたURLをデコード
  var lPa = e.parameter.lPa; //location.pathname
  var act = e.parameter.action; //action
  var eve = e.parameter.event; //event
  var lSe = e.parameter.lSe; //location.search
  lSe = decodeURIComponent(lSe); //POSTの際エンコードしたURLをデコード
  var lHa = e.parameter.lHa; //location.hash
  var dTi = e.parameter.dTi; //document.title
  var dRe = e.parameter.dRe; //document.referrer
  var nAc = e.parameter.nAc; //navigator.appCodeName
  var nAn = e.parameter.nAn; //navigator.appName
  var nAv = e.parameter.nAv; //navigator.appVersion
  var nCe = e.parameter.nCe; //navigator.cookieEnabled
  var nPl = e.parameter.nPl; //navigator.platform
  var nUa = e.parameter.nUa; //navigator.userAgent
  var nCp = e.parameter.nCp; //navigator.cpuClass
  var uLa = e.parameter.uLa; //language
  var cLa = e.parameter.cLa; //client language
  var sWi = e.parameter.sWi; //screen.width
  var sHe = e.parameter.sHe; //screen.height
  var wAv = e.parameter.wAv; //screen.availWidth
  var sAl = e.parameter.sAl; //screen.availHeight
  var sCl = e.parameter.sCl; //screen.colorDepth
  var sPi = e.parameter.sPi; //screen.pixelDepth
  sheet.appendRow([
    new Date(),
    lHr,
    lPa,
    act,
    eve,
    lSe,
    lHa,
    dTi,
    dRe,
    nAc,
    nAn,
    nAv,
    nCe,
    nPl,
    nUa,
    nCp,
    uLa,
    cLa,
    sWi,
    sHe,
    wAv,
    sAl,
    sCl,
    sPi
  ]);
}
```
## 【重要】 扱える情報について
これまで説明したりとおり、この方法はWeb公開したスプレッドシートに対し記録をしていくというものである。

つまり、スプレッドシートのkeyさえわかれば、誰でもスプレッドシートを閲覧することができてしまう。

筆者のような状況（フロントエンドしか扱えない環境）で、JavaScript側に直接記述する場合、扱う情報は他者から閲覧されても支障のないもののみにしなければならない。

たとえば、この方法ではフォームに入力された情報の記録も可能だが、個人情報を送信させるようなものだと、たちまち情報漏洩となってしまう。

## こんな応用もできる
スプレッドシートに記録してしまえば、あとはGASで様々なことができるようになる。

筆者の場合、月替りに直前1ヶ月分のアクセスログを別のスプレッドシートに保存し、かつ、任意のメールアドレスにExcel形式で自動送信している。

扱う情報に気を付ければ、使い勝手のいいアクセスログシステムの完成である。

参ったか、情シス。