Skip to content

Commit

Permalink
#22 metrics
Browse files Browse the repository at this point in the history
  • Loading branch information
Yegor Bugayenko committed Nov 20, 2016
1 parent 8f14bf0 commit ecd54f5
Show file tree
Hide file tree
Showing 8 changed files with 349 additions and 8 deletions.
11 changes: 9 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -141,10 +141,18 @@
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
</dependency>
<dependency>
<groupId>com.jcabi</groupId>
<artifactId>jcabi-xml</artifactId>
</dependency>
<dependency>
<groupId>com.jcabi</groupId>
<artifactId>jcabi-s3</artifactId>
</dependency>
<dependency>
<groupId>com.jcabi</groupId>
<artifactId>jcabi-aspects</artifactId>
Expand All @@ -162,8 +170,7 @@
<dependency>
<groupId>com.jcabi</groupId>
<artifactId>jcabi-dynamo</artifactId>
<version>1.0-SNAPSHOT</version>
<!-- <version>0.21.2</version> -->
<version>0.21.4</version>
</dependency>
<dependency>
<groupId>com.jcabi</groupId>
Expand Down
15 changes: 12 additions & 3 deletions src/main/java/io/jare/Entrance.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,11 @@
*/
package io.jare;

import com.jcabi.manifests.Manifests;
import com.jcabi.s3.Region;
import io.jare.cached.CdBase;
import io.jare.dynamo.DyBase;
import io.jare.model.Base;
import io.jare.tk.TkApp;
import java.io.IOException;
import org.takes.http.Exit;
Expand Down Expand Up @@ -51,9 +54,15 @@ private Entrance() {
* @throws IOException If fails
*/
public static void main(final String... args) throws IOException {
new FtCli(
new TkApp(new CdBase(new DyBase())), args
).start(Exit.NEVER);
final Base base = new CdBase(new DyBase());
new Logs(
base,
new Region.Simple(
Manifests.read("Jare-S3Key"),
Manifests.read("Jare-S3Secret")
).bucket("logs.jare.io")
);
new FtCli(new TkApp(base), args).start(Exit.NEVER);
}

}
202 changes: 202 additions & 0 deletions src/main/java/io/jare/Logs.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
/**
* The MIT License (MIT)
*
* Copyright (c) 2016 jare.io
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions: the above copyright notice and this
* permission notice shall be included in all copies or substantial
* portions of the Software. The software is provided "as is", without
* warranty of any kind, express or implied, including but not limited to
* the warranties of merchantability, fitness for a particular purpose
* and non-infringement. In no event shall the authors or copyright
* holders be liable for any claim, damages or other liability, whether
* in an action of contract, tort or otherwise, arising from, out of or
* in connection with the software or the use or other dealings in the
* software.
*/
package io.jare;

import com.jcabi.aspects.ScheduleWithFixedDelay;
import com.jcabi.aspects.Tv;
import com.jcabi.s3.Bucket;
import com.jcabi.s3.Ocket;
import io.jare.model.Base;
import io.jare.model.Domain;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.GZIPInputStream;
import org.apache.commons.lang3.StringUtils;

/**
* Logs in S3.
*
* @author Yegor Bugayenko (yegor@teamed.io)
* @version $Id$
* @since 0.7
*/
@ScheduleWithFixedDelay(delay = 1, unit = TimeUnit.HOURS)
final class Logs implements Runnable {

/**
* Parser of one line.
*/
private static final Pattern PTN = Pattern.compile(
StringUtils.join(
"(\\d{4}-\\d{2}-\\d{2})\t",
"\\d{2}:\\d{2}:\\d{2}\t",
"[A-Z0-9]+\t",
"(\\d+)\t",
"\\d+\\.\\d+\\.\\d+\\.\\d+\t",
"GET\t",
"[a-z0-9]+.cloudfront.net\t",
"/\t",
"\\d{3}\t",
"(https?://[^\t]+)\t"
)
);

/**
* Base.
*/
private final transient Base base;

/**
* Bucket.
*/
private final transient Bucket bucket;

/**
* Ctor.
* @param bse Base
* @param bkt Bucket
*/
Logs(final Base bse, final Bucket bkt) {
this.base = bse;
this.bucket = bkt;
}

@Override
public void run() {
try {
final Iterator<String> ockets = this.bucket.list("").iterator();
if (ockets.hasNext()) {
final String name = ockets.next();
this.process(name);
this.bucket.remove(name);
}
} catch (final IOException ex) {
throw new IllegalStateException(ex);
}
}

/**
* Process one ocket.
* @param name The name of the ocket
* @throws IOException If fails
*/
private void process(final String name) throws IOException {
final Ocket ocket = this.bucket.ocket(name);
final Path path = Files.createTempFile("jare", ".gz");
ocket.read(new FileOutputStream(path.toFile()));
final BufferedReader input = new BufferedReader(
new InputStreamReader(
new GZIPInputStream(
new FileInputStream(path.toFile())
)
)
);
final Map<String, Map<Date, Long>> map = new HashMap<>(0);
try {
while (true) {
final String line = input.readLine();
if (line == null) {
break;
}
Logs.parse(map, line);
}
} finally {
input.close();
}
for (final Map.Entry<String, Map<Date, Long>> entry : map.entrySet()) {
for (final Map.Entry<Date, Long> usg
: entry.getValue().entrySet()) {
final Iterator<Domain> domains =
this.base.domain(entry.getKey());
if (domains.hasNext()) {
domains.next().usage().add(
usg.getKey(),
usg.getValue()
);
}
}
}
}

/**
* Parse one line.
* @param map Map to populate
* @param line The line
*/
private static void parse(final Map<String, Map<Date, Long>> map,
final CharSequence line) {
final Matcher matcher = Logs.PTN.matcher(line);
if (matcher.find()) {
final String domain = URI.create(matcher.group(Tv.THREE)).getHost();
if (!map.containsKey(domain)) {
map.put(domain, new HashMap<>(0));
}
final Map<Date, Long> target = map.get(domain);
final Date date = Logs.asDate(matcher.group(1));
if (!target.containsKey(date)) {
target.put(date, 0L);
}
target.put(
date,
target.get(date) + Long.parseLong(matcher.group(2))
);
}
}

/**
* Convert text to date.
* @param txt Text
* @return A date
*/
private static Date asDate(final String txt) {
final TimeZone zone = TimeZone.getTimeZone("UTC");
final DateFormat fmt = new SimpleDateFormat(
"yyyy-MM-dd", Locale.ENGLISH
);
fmt.setTimeZone(zone);
try {
return fmt.parse(txt);
} catch (final ParseException ex) {
throw new IllegalStateException(ex);
}
}

}
3 changes: 2 additions & 1 deletion src/main/java/io/jare/fake/FkUsage.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
*/
package io.jare.fake;

import com.jcabi.log.Logger;
import io.jare.model.Usage;
import java.util.Date;
import java.util.SortedMap;
Expand All @@ -38,7 +39,7 @@ public final class FkUsage implements Usage {

@Override
public void add(final Date date, final long bytes) {
// nothing
Logger.info(this, "usage, date=%s, bytes=%d", date, bytes);
}

@Override
Expand Down
70 changes: 70 additions & 0 deletions src/test/java/io/jare/LogsTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/**
* The MIT License (MIT)
*
* Copyright (c) 2016 jare.io
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions: the above copyright notice and this
* permission notice shall be included in all copies or substantial
* portions of the Software. The software is provided "as is", without
* warranty of any kind, express or implied, including but not limited to
* the warranties of merchantability, fitness for a particular purpose
* and non-infringement. In no event shall the authors or copyright
* holders be liable for any claim, damages or other liability, whether
* in an action of contract, tort or otherwise, arising from, out of or
* in connection with the software or the use or other dealings in the
* software.
*/
package io.jare;

import com.jcabi.s3.Bucket;
import com.jcabi.s3.Ocket;
import io.jare.fake.FkBase;
import java.io.OutputStream;
import java.util.Collections;
import java.util.zip.GZIPOutputStream;
import org.apache.commons.io.IOUtils;
import org.junit.Test;
import org.mockito.Mockito;

/**
* Test case for {@link Logs}.
* @author Yegor Bugayenko (yegor@teamed.io)
* @version $Id$
* @since 1.0
* @checkstyle ClassDataAbstractionCouplingCheck (500 lines)
*/
public final class LogsTest {

/**
* Logs can unzip and log.
* @throws Exception If some problem inside
*/
@Test
public void unzipsAndLogs() throws Exception {
final Ocket ocket = Mockito.mock(Ocket.class);
Mockito.doAnswer(
inv -> {
final GZIPOutputStream gzip = new GZIPOutputStream(
OutputStream.class.cast(inv.getArgument(0))
);
IOUtils.copyLarge(
Logs.class.getResourceAsStream("test"),
gzip
);
gzip.close();
return null;
}
).when(ocket).read(Mockito.any(OutputStream.class));
final Bucket bucket = Mockito.mock(Bucket.class);
Mockito.doReturn(ocket).when(bucket).ocket(Mockito.anyString());
Mockito.doReturn(Collections.singleton("x")).when(bucket).list("");
new Logs(new FkBase(), bucket).run();
}

}
31 changes: 31 additions & 0 deletions src/test/java/io/jare/package-info.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* The MIT License (MIT)
*
* Copyright (c) 2016 jare.io
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions: the above copyright notice and this
* permission notice shall be included in all copies or substantial
* portions of the Software. The software is provided "as is", without
* warranty of any kind, express or implied, including but not limited to
* the warranties of merchantability, fitness for a particular purpose
* and non-infringement. In no event shall the authors or copyright
* holders be liable for any claim, damages or other liability, whether
* in an action of contract, tort or otherwise, arising from, out of or
* in connection with the software or the use or other dealings in the
* software.
*/

/**
* Jare, tests.
*
* @author Yegor Bugayenko (yegor@teamed.io)
* @version $Id$
* @since 0.7
*/
package io.jare;
Loading

0 comments on commit ecd54f5

Please sign in to comment.