Protchですよ。
これのね、Editorやら、ファームウエアやらつくってみましたよ。
みんな使ってください。
こんなイベントもやってるみたい!すごいね!Protch!
Protchですよ。
これのね、Editorやら、ファームウエアやらつくってみましたよ。
みんな使ってください。
こんなイベントもやってるみたい!すごいね!Protch!
だいぶん前から プログラミング教育を学校で開始するという話があり、2020年から義務教育で取り入れられるという事になりました。
そんな話もあり、Scratch+A4S+Arduinoを利用した教育用のテキスト(小学校高学年向け)を作ってみました。(ええ!もちろん作りかけです)
ScratchとA4s ( https://github.com/damellis/A4S )を組み合わせてArduino UNOを操作する環境を作り、その環境でのプログラミングを勉強するという内容にしています。
子供にプログラミングを教える、一緒にプログラミングを学ぶ時につかっていただければと思います。
#だれか資料を完成させてください!
福岡拠点野田です。
気が付けば涼しい季節になりましたが、いかがお過ごしでしょうか。
Go 言語について動けていなかったため、重い腰を上げて取り組みたいと思います。Go言語でよく使われているのは、WebAPIやメール送信・集計などのバッチ処理だと思います。javaも高速に処理ができますが重量級なイメージがあります。起動もすばやく、軽量で高速に並列処理ができるのは大きな魅力があります。
第1回目となる今回はインストールを行います。
https://golang.org/dl/
インストールは至って簡単。ダウンロードしたファイルを解凍して配置するだけ。
1 2 3 4 |
cd /usr/local wget https://dl.google.com/go/go1.11.1.linux-amd64.tar.gz tar zxvf go1.11.1.linux-amd64.tar.gz rm go1.11.1.linux-amd64.tar.gz |
.bash_profileなどにパスを追加してもらえればOKです。
1 2 3 |
export GOROOT=/usr/local/go export GOPATH=$HOME/go PATH=$PATH:$GOPATH/bin:$GOROOT/bin |
GOROOT というのがダウンロードして配置したGo自体のインストール先になります。
GOPATH というのが各プロジェクトフォルダに相当します。ここで開発モジュールの配置先となり、コンパイルしてできた実行ファイルが $GOPATH/bin 以下に配置されます。
Go言語初心者に向けてどのように開発を進めていくのがよいかについて深堀していこうと思います。
ちなみに go get してエラーがでるときは OS のモジュールバージョンが古い可能性が高いです。yum upgrade してすべてのモジュールを更新しておきましょう。
福岡拠点の香月です。
前回はLaravelのプロジェクトを作成しました。
今回はこれにDBへのアクセス設定と、モデルの追加及びDBのマイグレーションを行います。
DBへのアクセス設定はアプリケーションのルートフォルダにある.envファイルで行います。この.envファイルは.env.exampleを元に作成されます。
開発環境、検証環境、本番環境でそれぞれ異なる設定なのでその環境ごとのファイル.env.develop、.env.staging、.env.productionを作成することになるでしょう。リリース時にはリリース環境にあったファイルを.envにリネームします。
※Laravelでは.envはソース管理に含めないことがガイドラインに書いてあります。これはプロジェクトのルートフォルダにある.gitignoreを見ても明らかです。
さて .env を編集する前に、アプリケーションで使用する mysql のユーザーを作成します。また、使用するDBを作成します。
1 2 3 4 5 6 |
# mysql -uroot -p Enter password: mysql> create database scoresheet; mysql> GRANT SELECT,INSERT,UPDATE,DELETE,CREATE,DROP,ALTER,INDEX,TRIGGER ON scoresheet.* TO ss_user@localhost IDENTIFIED BY 'password'; mysql> GRANT SELECT ON *.* TO watcher@'%' IDENTIFIED BY 'readonly'; mysql> flush privileges; |
1行目でmysqlにrootユーザーでログインします。
3行目でアプリケーションで使用するデータベースを「scoresheet」という名前で作成します。
4行目ではデータベース使用するユーザーとして「ss_user@localhost」を作成しつつ、必要な権限設定を行います。
5行目では山椒の実のユーザーを作成します。
6行目で権限を反映します。
これでDBの準備が整いました。
.env を開くと、DBに関する設定を行う場所があるのでここに情報を記載します。
1 2 3 4 5 6 |
DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=scoresheet DB_USERNAME=ss_user DB_PASSWORD=password |
※本当の設定箇所は config/database.php です。しかし環境ごとに異なるなので.envに記載し、database.phpからは.envを参照するようになっています。
DBへのアクセス設定ができたので、ここからはモデルの作成とテーブルを追加する作業、マイグレーションです。アプリケーションのルートフォルダで
1 |
# php artisan make:model Score -m |
を実行すると モデルクラス用のファイルが作成されます。-mオプションでマイグレーションファイルも一緒に作成されるので、モデルを作成するときは指定しましょう。
app\Score.php
database/migrations/<日付_時間>_create_scores_table.php
モデル名を複数形の「スネークケース」にしたものが、テーブル名として使用されます。「Score」は「scores」になります。Laravelは内部に辞書をもっており、英単語を適切に複数形にしてくれます。ネイティブじゃない私にはありがたい機能です。
あとはこれを編集して必要なフィールドを追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
<?php use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateScoresTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('scores', function (Blueprint $table) { $table->increments('id'); $table->integer('student_id'); $table->integer('subject_id'); $table->timestamp('exam_day'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('scores'); } } |
これとは別にLaravelではプロジェクト作成時にusers、password_resetsテーブル用のマイグレーションファイルが作成されています。認証用ですね。
これと今作成したマイグレーションファイルのすべてが次のコマンドでまとめて
DBに登録されます。
1 2 3 4 5 6 7 |
# php artisan migrate Migrating: 2014_10_12_000000_create_users_table Migrated: 2014_10_12_000000_create_users_table Migrating: 2014_10_12_100000_create_password_resets_table Migrated: 2014_10_12_100000_create_password_resets_table Migrating: 2018_xx_xx_xxxxxx_create_scores_table Migrated: 2018_xx_xx_xxxxxx_create_scores_table |
「php artisan migrate:status」でマイグレーション結果を確認できます。
1 2 3 4 5 6 7 8 |
# php artisan migrate:status +------+------------------------------------------------+-------+ | Ran? | Migration | Batch | +------+------------------------------------------------+-------+ | Yes | 2014_10_12_000000_create_users_table | 1 | | Yes | 2014_10_12_100000_create_password_resets_table | 1 | | Yes | 2018_xx_xx_xxxxxx_create_scores_table | 1 | +------+------------------------------------------------+-------+ |
※一度もマイグレーションを実行していない状態で実行するとエラーが出てしまいます。
1 2 3 4 5 |
# php artisan migrate:status Illuminate\Database\QueryException : SQLSTATE[HY000] [1045] Access denied for user 'ss_user'@'localhost' (using password: YES) (SQL: select * from information_schema.tables where table_schema = scoresheet and table_name = migrations) |
「migrations」というテーブルが無い!と怒られるのですが、このテーブルは最初にマイグレーションを実行したときに作成されます。これを知らずにちょっとはまりました。
モデル及びテーブルがもっと必要な場合は「php artisan make:model xxx -m」を必要なだけ繰り返しましょう。最後に「php aritisan migrate」を忘れずに。
プログラムはこのモデルを使ってデータにアクセスしていきますよー。
現行ハイビジョンを超える超高画質を実現する、次世代の映像規格4K・8K。2018年12月1日から、4K放送・8K放送である「新4K8K衛星放送」が始まる。
※参考(総務省)
番組表関連アプリ実装に伴い、番組表関連の仕様についての確認を実施。
仕様は一般社団法人電波産業会(ARIB)にて規定されているが、無償公開はされていない。関連情報として、総務省が情報通信審議会の資料を公開している。
総務省 情報通信審議会情報通信技術分科会 放送システム委員会(第41回)
以下、TLVストリームを入手しておらず実環境未検証。考察レベルであり、認識違い誤記等あれば随時更新予定。
既存2K放送 | 4K放送・8K放送 | |
---|---|---|
方式 | MPEG-2 TS | MMT・TLV |
ストリーム | TSストリーム![]() |
TLVストリーム![]() |
放送局ロゴ | HDラージ HDスモール SD4:3ラージ SD4:3スモール SD16:9ラージ SD16:9スモール |
2K スモール ラージ |
※MH-EIT(MPEG-H Event Information Table)はEIT(Event Information Table)に規定されるテーブル、記述子と同機能を持つ。
放送局ロゴの扱いは考慮する必要があるが、その他についてはMPEG-2 TS方式と同機能を持つ規定となっている。
ミドルウェアの対応指針にも依存するが、アプリで違いを意識する必要はない模様。
福岡拠点の野田です。
秋雨や台風が気になりますが、過ごしやすい日々が増えてきたと思います。
今日は、Laravel の値検査の仕組みについて取り上げようと思います。
Laravel には値検査を行う設計の方向性として大きく3つの選択肢があります。
・FormRequest を継承してリクエストフォームオブジェクトを作る
・ValidatesRequestsトレイトを使う
・Validator ファサードを使う
Controller に対して Illuminate\Foundation\Http\FormRequest 型のメソッドインジェクションを指定しておくとそのメソッドに対応するURLが呼び出された際に値検査がかかるという仕組みになります。バリデーションの結果が false の場合、リクエスト値をセッションに保存して、リクエストを投げた前のページに戻ります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
/** * 入力画面を表示する(Request型は、バリデーションはかからない) * @param Request $request * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View */ public function edit(Request $request) { return $this->view('sample.input', $data); } /** * 入力確認画面を表示する(FormRequestを継承したクラスは入力検査がかかる) * @param MailUpdateRequest $request * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View */ public function editConfirm(MailUpdateRequest $request) { return $this->view('sample.check', $data); } /** * 完了画面を表示する(FormRequestを継承したクラスは入力検査がかかる) * @param MailUpdateRequest $request * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View */ public function update(MailUpdateRequest $request) { // DB更新処理など return $this->view('sample.done', $data); } |
とても便利な仕組みですが、都度、FormRequestを実装することになります。毎回似たようなコードを書くのは煩わしいですよね。そこで以下のような基底クラスを作成して継承して使いまわすことにします。この基底クラスは、自分のクラス名からバリデーションの設定を読み込んで動作する仕組みで動いています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
<?php namespace App\Http\Requests; use Illuminate\Foundation\Http\FormRequest; use Log; /** * フォームリクエストのベースクラス * ※継承して使います。 * Class BaseRequest * @package App\Http\Requests */ class BaseRequest extends FormRequest { protected $className = null; /** * クラス名を取得保持 * @return null|string */ public function getClass() { if (is_null($this->className)) { $className = preg_replace('/^.+\\\\/', '', get_class($this)); $this->className = $className; } return $this->className; } /** * リクエストに対する認証可否 * @return bool */ public function authorize() { return true; } /** * 入力検査ルール * @return array */ public function rules() { return config('validation.'.$this->getClass().'.rules'); } /** * 項目名 * @return array */ public function attributes() { return config('validation.'.$this->getClass().'.attributes'); } /** * 特別エラーメッセージ * @return array|mixed */ public function messages() { return config('validation.'.$this->getClass().'.messages'); } } |
以下は、config/validation.php の記述例です。FormRequest実装クラス名をキーに設定を記述するとそれを読み込んでくれる仕掛けになっています。クラスの型を利用しつつ、リクエスト値の検査は設定ファイルを利用してカスタマイズしやすくします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<?php return [ 'MailUpdateRequest' => [ 'rules' => [ 'email' => 'required|max:100', 'email_conf' => 'required|same:email|max:100', 'confirm' => 'required', ], 'attributes' => [ 'email' => 'メールアドレス', 'email_conf' => 'メールアドレス(確認)', ], 'messages' => [ 'confirm.required' => 'チェックを入れてください', ], ], ]; |
さらにテンプレート側に目を向けてみましょう。入力フォームのテンプレートは、エラー表示と「戻る」ボタンで遷移の両方を共通で実現している場合がほとんどだと思います。Laravel で用意されている old ヘルパーメソッドで取得できるのは、エラー時のセッションしかありませんので、以下のような記述をすることでエラー時のセッション値があれば、セッション値、なければリクエスト値をとる実装が可能です(old メソッドのデフォルト値にリクエスト値を設定する実装になります)。
1 |
{{ old('email',$request->get('email')) }} |
エラーを表示する場合は以下で対応できます。
1 |
{!! $errors->first('email', '<span class="error">:message</span>') !!} |
$errors->has(’email’)や$errors->count()などもよく使うメソッドだと思います。エラーは MessageBag というクラスで定義されていますので、以下を参考にしていただければ幸いです。
https://laravel.com/api/5.6/Illuminate/Support/MessageBag.html
FormRequest の処理のうち Controller 側の処理を切り出したものが ValidatesRequest になります。
Controller で use ValidatesRequest をすることで Controller 中で $this->validate() メソッドなどが使えるようになります。
以下 Laravel マニュアルページより
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
/** * Store the incoming blog post. * * @param Request $request * @return Response */ public function store(Request $request) { $this->validate($request, [ 'title' => 'required|unique|max:255', 'body' => 'required', ]); // } |
値検査に失敗すると Illuminate\Contracts\Validation\ValidationException が発生し、FormRequest 同様、前のページに戻ります。validateをかけるタイミングを調整できるため、リクエスト値の前処理が必要な場面や FormRequest の作成をしたくない場合に使われると思いますが、Validator ファサードの選択肢もあるため、相対的に利用する場面は少ないと思われます。
Validator ファサードを使えば値検査部分だけを部品化して値検査をすることが可能です。バッチ系で値検査を行うときは直に呼び出して使うことが多いと思います。例えばCSVインポートバッチの入力値チェックなどはこの Validatorファサードの出番です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
$input = $request->all(); $rules = config('validation.'.$key .'.rules'); $attributes = config('validation.'.$key .'.attributes'); $messages = config('validation.'.$key .'.messages'); $validator = Validator::make( $input, $rules, array_merge(config('validation.common.errors'), $messages), $attributes ); if ($validator->fails()) { // エラー処理 } // 正常処理 |
上記は、config/validation.php で設定を外だししている例です。個別実装することももちろん可能ですが、ある程度共通化できるところは共通化しておくと良いでしょう。
なんだかんだいいましたが、場面場面に応じて最適な値検査の方法を選択して実装していくとよいでしょう。その中で特に伝えたかったことが「設定ファイル化」という部分になります。値検査(バリデーション)というプログラムの課題の中でconfig/validation.php にまとめておく、HTML部品という課題の中で config/form.php にまとめておく、アプリケーション設定として、config/const.php にまとめておく、などなどです。後で入力チェックをまとめるときに個別の実装をみなくてよいのは、大きな助けになると思います。まだの方は、実装時の良い習慣としてプログラムの課題の設定ファイル化を是非やってみてください。
以下補足になります。入力、確認、完了のような遷移がある場合のルーティングのTIPSです。通常、入力画面は、GETリクエスト、それ以外はPOSTリクエストとしていると思います。つい忘れがちですが、保存ページについては、GETも受け付けて入力画面に逃がすとよいと思います。戻すときの遷移でリロードして、MethodNotAllowedのエラーが見えてしまうとちょっとカッコ悪いですよね。
1 2 3 4 5 6 |
Route::get('/mail/edit', 'MemberController@edit'); Route::post('/mail/edit', 'MemberController@editConfirm'); Route::post('/mail/update', 'MemberController@update'); // URLを直叩きされたら、入力画面へ Route::get('/mail/update', 'MemberController@edit'); |
福岡拠点の香月です。
Windows環境で動作するPython3.6.6の拡張モジュールをC++で作りました。
開発ツールはVisual Studio 2015です。
早速作成した拡張モジュールのDebug版を使ってみようとPythonインタプリタを起動してモジュールロードしたんですが゙………
1 2 3 4 5 6 7 |
C:\work\PythonExtension>python Python 3.6.6 (v3.6.6:4cf1f54eb7, Jun 27 2018, 02:47:15) [MSC v.1900 32 bit (Intel)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> import spam Fatal Python error: PyThreadState_Get: no current thread C:\work\PythonExtension>python |
とこのようにエラーが発生してしまいました。インタプリタも強制終了しているようです。
これは拡張モジュールがDebug版なので、Pythonバイナリもデバッグ版を使う必要があります。python_d.exeがpython.exeと同じ場所にあるのでそれを使いましょう。
また、拡張モジュール名にも「_d」が必要です。Release版が「spam.pyd」ならDebug版では「spam_d.pyd」です。リネームで十分です。
1 2 3 4 5 |
C:\work\PythonExtension>python_d Python 3.6.6 (v3.6.6:4cf1f54eb7, Jun 27 2018, 02:51:19) [MSC v.1900 32 bit (Intel)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> import spam >>> |
このようにFatal Python Errorは発生せずに続行できます。
Debug版でのデバッグをあきらめて、Release版に無理やりデバッグ情報をつけてデバッグしていた方、是非これをお試しあれ。
これに気付く前にVisual Studio 2017のドキュメントで次のようなものを見つけていました。
https://docs.microsoft.com/ja-jp/visualstudio/python/working-with-c-cpp-python-in-visual-studio?view=vs-2017
以下抜粋
警告
デバッグ構成の場合でも [C/C++] > [コード生成] > [ランタイム ライブラリ] のオプションを常にマルチスレッド DLL (/MD) に設定します。これは、この設定がデバッグ以外の Python バイナリのビルドに使用されるためです。 |
でもこんなことする必要なく、python_d.exeを使うだけでOKですよー。
福岡拠点の野田です。
暑い日が続きますが、いかがお過ごしでしょうか。
Skypeが新しくなって、だいぶ使いにくくなったなぁ、と感じます。履歴をコピーしたり検索することも不自由に感じることが多くなりました。備忘録的に残していた書き込みをWikiに転載するため、どうにかまとめてコピーすることができないかと思い、少し調べてみました。
現在 2018/08/06 Windows バージョンの Skype 履歴は以下のファイルに格納されていることを確認しました。
%localappdata%\Packages\Microsoft.SkypeApp_kzf8qxf38zg5c\LocalState\\skype.db
参考:Q.how do I export my chat history?
https://answers.microsoft.com/en-us/skype/forum/skype_win10-skype_messms-skype_instamessms/how-do-i-export-my-chat-history/20849b44-d68a-40f4-8cb8-6a2dc88e9e7e
テキストエディタで開いてみたところ先頭に「SQLite format 3」と記載されているではないですか。SQLiteは、パブリックドメインの軽量なRDBシステムです。SQLをサポートしており、Skypeに限らず、いろいろなアプリケーションのデータ保持に利用されています。
SQLiteファイルへの接続ですが、ODBCで接続する方法もありますが、今回はみんなが大好きな「A5:SQL Mk-2」を使っての接続を紹介します。A5:SQL Mk-2は、ER図も描けるとても優れたデータベースツールです。今回はこれを使ってファイルを参照します。
A5:SQL Mk-2
https://a5m2.mmatsubara.com/
step 1. データベースに追加
step 2. 左下の追加ボタンを押下
step 3. SQLiteを選択
step 4. skype.db を選択します。直に接続すると破損する可能性があるため、コピーしたファイルに繋ぎましょう。
step 5.ログイン認証は表示されますが、そのまま接続できます。
step 6. messages が書き込みテーブルです。
これでスレッドの書き込みの検索、編集も思いのままです。
無事、スレッドからWikiへの転載も完了することができました。
福岡拠点の宮里です。
先日、アパッチのアクセスログをアーカイブして一定期間保存する作業を行っていたのですが、
ログを確認していると、何か違和感が…
|02/May/2018:09:24:59 +0000|
私「あれ?もう出社して9時間は経ってて、もう今日も終わっちまうのかなぁって気がしていたけど」
心の中の金子賢「バカヤロー、まだ始まってもいねーよ」
あれあれあれと心を鎮めながら、サーバーに設定されているタイムゾーンを確認してみると、
1 2 3 4 5 6 |
strings /etc/localtime #出力結果 TZif2 TZif2 UTC0 #←これ |
このサーバーのタイムゾーンはUTC。
このままだとなにかと不都合です。
心臓にもわるいので、JST時間帯へ変更したいと思います。
sysconfigディレクトリの設定ファイル(clock)からタイムゾーンを修正します。
ひとまずバックアップ取って、
1 2 3 4 5 6 7 8 |
cp /etc/sysconfig/clock{,.orig} #/etc/sysconfig/clockの修正 vi /etc/sysconfig/clock #下記に追記修正 ZONE="Asia/Tokyo" UTC=false |
シンボリックリンクの向き先Asia/Tokyoへ変更します。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime #確認 ls -l /etc/localtime /etc/localtime -> /usr/share/zoneinfo/Asia/Tokyo #設定変更した時刻を確認 strings /etc/localtime #出力結果 TZif2 TZif2 JST-9 #←これ |
サーバーのタイムゾーンがUTCからJSTに変わりました。
これでひと安心かと思いきや、
この作業を行う前にすでに記録されているログの時刻をJSTに修正しないと何かと収まりがわるいです。
なので、USTで記録されているログをJSTへと変換するスクリプトを作りたいと思います。
スクリプトは、サーバーに標準でインストールされているpythonで作成してライブラリをひとつだけ追加します。
pytzの取得
1 2 3 4 5 6 7 8 |
cd /適当なディレクトリ/ wget https://files.pythonhosted.org/packages/10/76/52efda4ef98e7544321fd8d5d512e11739c1df18b0649551aeccfb1c8376/pytz-2018.4.tar.gz tar zxvf pytz-2018.4.tar.gz cd pytz-2018.4 # 解凍したディレクトリから本体をコピー cp pytz-2018.4/pytz /作業ディレクトリ/ |
JSTへ変換するスクリプト
changeJst.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 |
# /usr/bin/python # coding: utf-8 import sys import os import shutil import re import datetime import pytz #ディレクトリへのパス logDirPath = '/ログのディレクトリパス/' #正規表現 #パスにマッチ fileNamePtn = '(access|error)_log\.[0-9]{8}$' #ファイル名にマッチ fileName_ptn = '.*fileName.*(access|error)_log\.[0-9]{8}$' #access_logにマッチさせる accsNamePtn = 'access_log\.[0-9]{8}$' #error_logにマッチさせる errNamePtn = 'error_log\.[0-9]{8}$' #アクセスログ内のUTC時刻マッチ #例:[22/May/2018:08:21:01 +0000] accs_ptn = '[0-9]{2}/\D{3}/[0-9]{4}:[0-9]{2}:[0-9]{2}:[0-9]{2}\s\+[0-9]{4}' #実はJST⁇の行のチェック accs_ptn_jst = '[0-9]{2}/\D{3}/[0-9]{4}:[0-9]{2}:[0-9]{2}:[0-9]{2}\s\+0900' #エラーログの時刻にマッチ #例:[Tue Jun 07 15:34:25.126709 2670] error_ptn = '\D{3}\s\D{3}\s[0-9]{2}\s[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{6}\s2[0-9]{3}' def changeJST(string): result = "" jst_time = "" #もしJST時刻ならスルー if re.search(accs_ptn_jst, string): return None #アクセスログにマッチ elif re.search(accs_ptn, string): ff = re.search(accs_ptn, string) utc = [] #月 ffDec = re.search('/\D{3}', ff.group(0)) utc.append(ffDec.group(0)[1:]) #日 ffDec = re.search('[0-9]{2}', ff.group(0)) utc.append(ffDec.group(0)) #時間 ffDec = re.search(':[0-9]{2}:[0-9]{2}:[0-9]{2}', ff.group(0)) utc.append(ffDec.group(0)[1:]) #時差 ffDec = re.search('\+[0-9]{4}', ff.group(0)) utc.append(ffDec.group(0)) #年 ffDec = re.search('2[0-9]{3}', ff.group(0)) utc.append(ffDec.group(0)) #並べ替え #形式:Nov 29 06:08:08 +0000 2006 result = "%s %s %s %s %s" % (utc[0], utc[1], utc[2], utc[3], utc[4]) utcDate = datetime.datetime.strptime(result, '%b %d %H:%M:%S +0000 %Y') jstTimezone = pytz.timezone('Asia/Tokyo') #Asia/Tokyoへ変換後、元のログフォーマットへ戻す jst_time = jstTimezone.fromutc(utcDate) #形式[07/Jun/2018:01:47:12 +0900] jst_time = jst_time.strftime('%d/%b/%Y:%X %z') return jst_time #エラーログにマッチ elif re.search(error_ptn, string): ff = re.search(error_ptn, string) utc = [] #曜日 ffDec = re.search('^\D{3}\s', ff.group(0)) utc.append(ffDec.group(0)[:-1]) #月 ffDec = re.search('\s\D{3}\s', ff.group(0)) utc.append(ffDec.group(0)[1:][:-1]) #日 ffDec = re.search('\s[0-9]{2}\s', ff.group(0)) utc.append(ffDec.group(0)[1:][:-1]) #時間 ffDec = re.search('\s[0-9]{2}:[0-9]{2}:[0-9]{2}', ff.group(0)) utc.append(ffDec.group(0)[1:]) #id ffDec = re.search('\.[0-9]{6}', ff.group(0)) utc.append(ffDec.group(0)) #年 ffDec = re.search('\s2[0-9]{3}', ff.group(0)) utc.append(ffDec.group(0)[1:]) #エラーログにマッチ #配列の中身の例:['Thu', 'Jun', '07', '06:35:13', '.378829', '2018'] #並べ替え #形式:Nov 29 06:08:08 +0000 2006 result = "%s %s %s +0000 %s" % (utc[1], utc[2], utc[3], utc[5]) utcDate = datetime.datetime.strptime(result, '%b %d %H:%M:%S +0000 %Y') jstTimezone = pytz.timezone('Asia/Tokyo') #Asia/Tokyoへ変換後、元のログフォーマットへ戻す jst_error = jstTimezone.fromutc(utcDate) #形式:[Tue Jun 05 07:44:15.465239 2018] #Thu Jun 07 15:35:13 2018 jst_time = jst_error.strftime('%a %b %d ') jst_time += jst_error.strftime('%X') jst_time += utc[4] jst_time += jst_error.strftime(' %Y') return jst_time else: return None #標準入力から読込 for line in sys.stdin: #オリジナルログの保存ディレクトリの作成 #保存ディレクトリの存在チェック後、存在しなければmkdir if os.path.isdir("%sfileName" % logDirPath) is not True: os.makedirs("%sfileName" % logDirPath) #行末の改行を削除 line = line.rstrip() #Readでオープン file = open(line, "r") newFileName = "" #念のためファイルの存在チェック後、 #読み込んだファイルを保存ディレクトリへ移動 if os.path.exists(line): fileName = re.search(fileNamePtn, line) fName = fileName.group(0) if re.search(fileName_ptn, line): fPath = "%sfileName/%s" % (logDirPath, fName) shutil.move(line, fPath) #読み込んだファイルを1行ずつ処理 for row in file: #UTCからJSTへ変換する関数。JSTに変換された日付の文字列が返ってくる toJst = changeJST(row) utcToJst = "" if toJst is not None: if re.search(accsNamePtn, line): utcToJst = re.sub(accs_ptn, toJst, row) elif re.search(errNamePtn, line): utcToJst = re.sub(error_ptn, toJst, row) #新規ファイルへの書き込み。無限ループ回避の名前変更 newFileName = line + ".log" newfile = open(newFileName, "a") newfile.write(utcToJst) newfile.close() file.close() #元のファイル名へ戻す if os.path.isfile(newFileName): os.rename(newFileName, line) else: #ファイル全体で修正が無かったときに保存ディレクトリにmvしたファイルを戻す if re.search(fileName_ptn, line): fPath = "%sfileName/%s" % (logDirPath, fName) shutil.copy2(fPath, line) |
粗いスクリプトですが、ひとまず期待通りに動くことが確認できましたので、作業するサーバーのhttpdを停止してスクリプトを実行しました。ちなみにAWSのELBがunhealthyを認識して振り分けを開始するまで約1分~2分ほどかかるようです。認識する前にhttpdを止めると502で振り分けもされなくなるので注意が必要です。
処理するディレクトリのバックアップを取って実行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#処理するファイル数の確認 find /ディレクトリ/ -type f | grep -E '.*(access|error)_log\.[0-9]{8}$' -c #スクリプト実行 find /ディレクトリ/ -type f | grep -E '.*(access|error)_log\.[0-9]{8}$' | python /スクリプトのディレクトリ/changeJst.py > log.txt #エラーチェックスクリプト実行 find /ディレクトリ/ -type f | grep -E '.*(access|error)_log\.[0-9]{8}$' | python /ディレクトリ/errCheck.py > errCheck0308.txt #処理後のファイル数の確認 find /ディレクトリ/ -type f | grep -E '.*(access|error)_log\.[0-9]{8}$' -c #差分チェック diff -s -q -r /ディレクトリ/ /バックアップディレクトリ/ > diff.txt diff -y /ディレクトリ/access_log.20180304 /バックアップディレクトリ/access_log.20180304.log > diff-fileName_log.txt |
なんとか完了です。
サーバー時刻もUTCからJSTへ変更します。
その後、httpdを再起動。
無事に現在時刻が18時に修正されました。
やっと終わっちまえます。
それから様子をみること数日。
dateコマンドやログに出力される時刻はしっかりUTCからJSTに変更がされているけど、cronに設定したタスクの実行時間がおかしい!どうにも9時間遅れて実行されている!と気づきました。
調べてみるとサーバーのタイムゾーンを変更しただけではcronの実行時刻への反映がされないようです。crondを再起動してJSTタイムゾーンの反映が必要でした。。。
crontabの設定ファイルへtimezoneを記述して、crondの再起動。
1 2 3 4 5 6 7 8 9 |
vi /etc/crontab ------ #下記の行を追記 CRON_TZ=Asia/Tokyo ------ #crondの再起動 service crond restart |
これでcronの実行時間にもJSTが反映されました。
福岡拠点の香月です。
前回まででLaravel実行におけるインフラ整備が終わりました。
今回はLaravelプロジェクトの作成です。
環境は引き続きこちらで構築したCentOS上です。
Windowsのコマンドプロンプトを立ち上げて次のコマンドでCentOSを起動。
1 |
C:\vm\centos72>vagrant up |
ターミナルソフトでローカルポート2222にsshでアクセスします。
以降の操作はターミナル上で行います。
実行ユーザーはvagrantです。実行はホームディレクトリで行いました
プロジェクトを作成するには次のコマンドを実行します。プロジェクト名は「scoresheet」とします。
1 |
# composer create-project "laravel/laravel" scoresheet |
以下が出力されます。
1 2 3 4 5 6 |
Installing laravel/laravel (v5.6.21) - Installing laravel/laravel (v5.6.21): Loading from cache : : > @php artisan key:generate Application key [base64:qearp8v/iqgPEEoRZjcnwFlg5ge06kWWnsAScNSaWJg=] set successfully. |
カレントディレクトリに「scoresheet」というフォルダが作成され、そこにLaravelの構成ファイルが作成されます。
このコマンドでは最新バージョンがインストールされますが、プロジェクトによってはバージョンを指定してインストールしなければならないことがあります。例えば5.1をインストールする場合はこう。
1 |
# composer create-project "laravel/laravel=5.1.*" scoresheet |
バージョンの3桁目は必ず*にしましょう。
インストールされたバージョンは次のコマンドで確認できます。作成されたディレクトリに移動して実行しましょう。
1 2 |
# php artisan -V Laravel Framework version 5.1.46 (LTS) |
プロジェクトの作成コマンドとして本家サイトでは「# laravel new <プロジェクト名>」があります。しかしWindows環境では正しく動作しない報告が沢山あるようなので、「# composer create-project」コマンドで統一するのが良いようです。
次はWebサーバーの設定です。rootになって作成したディレクトリをWebサーバーのディレクトリの下に移動します。
1 2 |
# sudo -s # mv /home/vagrant/scoresheet /etc/httpd/scoresheet |
/etc/httpdはvagrantユーザーには書き込み権限がないため、rootで作業します。
ユーザーvagrantで/etc/httpdに移動して直接「composer create-project」コマンドを実行すればいいのでは?とも考えますが、vagrantには/etc/httpdへの書き込み権限がないためLaraveのプロジェクトの種ファイル であるhttp://cabinet.laravel.com/latest.zip のダウンロードに失敗してしまいます。
続けてLaravelホームをWebのルートにするためにhttpd.conを編集します。
1 2 3 4 5 6 7 8 9 |
# vi /etc/httpd/conf/httpd.conf DocumentRoot "/etc/httpd/scoresheet/public" <Directory "/etc/httpd/scoresheet/public"> Options Indexes FollowSymLinks AllowOverride None Require all granted </Directory> |
※厳密にはWebサーバーのディレクトリ以下に必ずしも移動させるひつようはありません。httpd.confの設定でLaravelディレクトリへのエイリアスを設定し、そこをDocumentRootに設定するのでもいいでしょう。
次にWebサーバーがLaravelのstorage、bootstrap/cacheディレクトリに書き込みできるようにパーミッションを調整します。
1 2 |
# cd /etc/httpd/scoresheet # chmod -R 777 storage bootstrap/cache |
最後にWebサーバーを再起動します。
1 |
# systemctl restart httpd |
再起動が完了したらホストOS(Windows)上のブラウザからルートにアクセスします。
http://localhost:8080/
Laravelロゴが表示されれば完成です。