コード全体は、このような感じ。解説はコードの後に書いてます。
use std::fs::File;
use std::io::{BufReader, BufRead};
use std::collections::HashMap;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
#[derive(Debug, Serialize, Deserialize)]
struct LogItem {
method: String,
path: String,
code: isize,
size: isize,
}
fn main() -> Result<(), Box<std::error::Error>> {
let mut scores = HashMap::new();
let f = "/path/to/file";
for result in BufReader::new(File::open(&f)?).lines() {
let l = result?;
let log: LogItem = serde_json::from_str(&l).unwrap();
//println!("{:?}",log);
let count = scores.entry(log.path).or_insert(0);
*count += 1;
}
for(k, v) in &scores {
println!("{} {}", k, v);
}
Ok(())
}
ここから動作環境とコード解説。
まず、apache-loggenでJSON形式で出力したファイルを用意する。
gem install apache-loggen
apache-loggen --limit=1000 --json > test.json
こんな感じのログが出力される。
{"host":"224.117.48.63","user":"-","method":"GET","path":"/category/software","code":200,"referer":"/item/electronics/2875","size":41,"agent":"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11"}
Rustでは、こんな感じの構造体を用意する。集計したいものだけ用意すればいい。
struct LogItem {
method: String,
path: String,
code: isize,
size: isize,
}
ファイルから1行読むたびにJSONとしてパースする。改行が入り複数の行にまたがるJSONは、ちょっと苦労しそうだ。(どれぐらいあるのかな?)
let mut scores = HashMap::new();
let f = "/path/to/file";
for result in BufReader::new(File::open(&f)?).lines() {
let l = result?;
let log: LogItem = serde_json::from_str(&l).unwrap();
//println!("{:?}",log);
let count = scores.entry(log.path).or_insert(0);
*count += 1;
}
最後に、Hashmapの中身をforループでハッシュマップのキーと値のペアを走査する。
for(k, v) in &scores {
println!("{} {}", k, v);
}
追記ですが、reqwestでも簡単に書けますね。
extern crate reqwest;
use std::collections::HashMap;
fn main() -> Result<(), Box<std::error::Error>> {
let resp: HashMap<String, String> = reqwest::get("https://httpbin.org/ip")?
.json()?;
//println!("{:#?}", resp);
println!("{}", resp["origin"]);
Ok(())
}