[
  {
    "path": ".editorconfig",
    "content": "#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\nroot = true\n\n[*]\ncharset = utf-8\nend_of_line = lf\ninsert_final_newline = true\ntrim_trailing_whitespace = true\nindent_style = space\n\n[*.java]\nindent_size = 4\n\n[*.xml]\nindent_size = 2\n\n# Tab indentation (no size specified)\n\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "github: nielsbasjes\ncustom: https://www.paypal.me/nielsbasjes\n"
  },
  {
    "path": ".github/workflows/build.yml",
    "content": "#\n# Apache HTTPD & NGINX Access log parsing made easy\n# Copyright (C) 2011-2023 Niels Basjes\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nname: Logparser\n\non:\n  push:\n    branches: [ main ]\n  pull_request:\n    branches: [ main ]\n\njobs:\n  build:\n    name: Build with Java ${{ matrix.java }}\n    runs-on: ubuntu-latest\n\n    steps:\n    - name: Checkout sourcecode\n      uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n\n    - name: Cache Local Maven Repository\n      uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5\n      with:\n        path: ~/.m2/repository\n        key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}\n        restore-keys: |\n          ${{ runner.os }}-maven-\n\n\n    - name: 'Setup: Install JDK 21, 25'\n      uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0\n      with:\n        distribution: 'temurin'\n        java-version: |\n          21\n          25\n\n    - name: Build Logparser\n      run: mvn -B clean package\n\n    - name: Codecov\n      uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6.0.0\n      with:\n        token: ${{ secrets.CODECOV_TOKEN }}\n"
  },
  {
    "path": ".gitignore",
    "content": "*.class\n\n# Maven\ndependency-reduced-pom.xml\n\n# Package Files #\n*.jar\n*.war\n*.ear\n\ntarget\n*.bak\n*.swp\n*~\nbin\n__*\n\n#Eclipse stuff\n.metadata\n.settings\n.classpath\n.project\nhs_err_pid*.log\n\n#IntelliJ stuff\n.idea\n*.iml\n\n#test files\nx\nxx\ny\nyy\nz\nzz\n\n#Hive/Hadoop logs\nSecurityAuth-*.audit\nhadoop*.log\n*-audit.log\nrm-appsummary.log\n\n# Docker\ndevtools/docker/_m2/\ndevtools/docker/_gnupg/\n"
  },
  {
    "path": ".pmd",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n Apache HTTPD & NGINX Access log parsing made easy\n Copyright (C) 2011-2023 Niels Basjes\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n https://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n-->\n<pmd>\n    <useProjectRuleSet>false</useProjectRuleSet>\n    <ruleSetFile>.ruleset</ruleSetFile>\n    <rules>\n        <rule>\n            <name>AvoidDecimalLiteralsInBigDecimalConstructor</name>\n            <ruleset>Basic Rules</ruleset>\n        </rule>\n        <rule>\n            <name>AvoidMultipleUnaryOperators</name>\n            <ruleset>Basic Rules</ruleset>\n        </rule>\n        <rule>\n            <name>AvoidThreadGroup</name>\n            <ruleset>Basic Rules</ruleset>\n        </rule>\n        <rule>\n            <name>AvoidUsingHardCodedIP</name>\n            <ruleset>Basic Rules</ruleset>\n        </rule>\n        <rule>\n            <name>AvoidUsingOctalValues</name>\n            <ruleset>Basic Rules</ruleset>\n        </rule>\n        <rule>\n            <name>BigIntegerInstantiation</name>\n            <ruleset>Basic Rules</ruleset>\n        </rule>\n        <rule>\n            <name>BooleanInstantiation</name>\n            <ruleset>Basic Rules</ruleset>\n        </rule>\n        <rule>\n            <name>BrokenNullCheck</name>\n            <ruleset>Basic Rules</ruleset>\n        </rule>\n        <rule>\n            <name>CheckResultSet</name>\n            <ruleset>Basic Rules</ruleset>\n        </rule>\n        <rule>\n            <name>ClassCastExceptionWithToArray</name>\n            <ruleset>Basic Rules</ruleset>\n        </rule>\n        <rule>\n            <name>CollapsibleIfStatements</name>\n            <ruleset>Basic Rules</ruleset>\n        </rule>\n        <rule>\n            <name>DoubleCheckedLocking</name>\n            <ruleset>Basic Rules</ruleset>\n        </rule>\n        <rule>\n            <name>EmptyCatchBlock</name>\n            <ruleset>Basic Rules</ruleset>\n        </rule>\n        <rule>\n            <name>EmptyFinallyBlock</name>\n            <ruleset>Basic Rules</ruleset>\n        </rule>\n        <rule>\n            <name>EmptyIfStmt</name>\n            <ruleset>Basic Rules</ruleset>\n        </rule>\n        <rule>\n            <name>EmptyInitializer</name>\n            <ruleset>Basic Rules</ruleset>\n        </rule>\n        <rule>\n            <name>EmptyStatementNotInLoop</name>\n            <ruleset>Basic Rules</ruleset>\n        </rule>\n        <rule>\n            <name>EmptyStaticInitializer</name>\n            <ruleset>Basic Rules</ruleset>\n        </rule>\n        <rule>\n            <name>EmptySwitchStatements</name>\n            <ruleset>Basic Rules</ruleset>\n        </rule>\n        <rule>\n            <name>EmptySynchronizedBlock</name>\n            <ruleset>Basic Rules</ruleset>\n        </rule>\n        <rule>\n            <name>EmptyTryBlock</name>\n            <ruleset>Basic Rules</ruleset>\n        </rule>\n        <rule>\n            <name>EmptyWhileStmt</name>\n            <ruleset>Basic Rules</ruleset>\n        </rule>\n        <rule>\n            <name>ForLoopShouldBeWhileLoop</name>\n            <ruleset>Basic Rules</ruleset>\n        </rule>\n        <rule>\n            <name>JumbledIncrementer</name>\n            <ruleset>Basic Rules</ruleset>\n        </rule>\n        <rule>\n            <name>MisplacedNullCheck</name>\n            <ruleset>Basic Rules</ruleset>\n        </rule>\n        <rule>\n            <name>OverrideBothEqualsAndHashcode</name>\n            <ruleset>Basic Rules</ruleset>\n        </rule>\n        <rule>\n            <name>ReturnFromFinallyBlock</name>\n            <ruleset>Basic Rules</ruleset>\n        </rule>\n        <rule>\n            <name>UnconditionalIfStatement</name>\n            <ruleset>Basic Rules</ruleset>\n        </rule>\n        <rule>\n            <name>UnnecessaryConversionTemporary</name>\n            <ruleset>Basic Rules</ruleset>\n        </rule>\n        <rule>\n            <name>UnnecessaryFinalModifier</name>\n            <ruleset>Basic Rules</ruleset>\n        </rule>\n        <rule>\n            <name>UnnecessaryReturn</name>\n            <ruleset>Basic Rules</ruleset>\n        </rule>\n        <rule>\n            <name>UnusedNullCheckInEquals</name>\n            <ruleset>Basic Rules</ruleset>\n        </rule>\n        <rule>\n            <name>UselessOperationOnImmutable</name>\n            <ruleset>Basic Rules</ruleset>\n        </rule>\n        <rule>\n            <name>UselessOverridingMethod</name>\n            <ruleset>Basic Rules</ruleset>\n        </rule>\n        <rule>\n            <name>ForLoopsMustUseBraces</name>\n            <ruleset>Braces Rules</ruleset>\n        </rule>\n        <rule>\n            <name>IfElseStmtsMustUseBraces</name>\n            <ruleset>Braces Rules</ruleset>\n        </rule>\n        <rule>\n            <name>IfStmtsMustUseBraces</name>\n            <ruleset>Braces Rules</ruleset>\n        </rule>\n        <rule>\n            <name>WhileLoopsMustUseBraces</name>\n            <ruleset>Braces Rules</ruleset>\n        </rule>\n        <rule>\n            <name>CloneThrowsCloneNotSupportedException</name>\n            <ruleset>Clone Implementation Rules</ruleset>\n        </rule>\n        <rule>\n            <name>ProperCloneImplementation</name>\n            <ruleset>Clone Implementation Rules</ruleset>\n        </rule>\n        <rule>\n            <name>CyclomaticComplexity</name>\n            <ruleset>Code Size Rules</ruleset>\n        </rule>\n        <rule>\n            <name>ExcessiveClassLength</name>\n            <ruleset>Code Size Rules</ruleset>\n        </rule>\n        <rule>\n            <name>ExcessiveMethodLength</name>\n            <ruleset>Code Size Rules</ruleset>\n        </rule>\n        <rule>\n            <name>ExcessiveParameterList</name>\n            <ruleset>Code Size Rules</ruleset>\n        </rule>\n        <rule>\n            <name>ExcessivePublicCount</name>\n            <ruleset>Code Size Rules</ruleset>\n        </rule>\n        <rule>\n            <name>NcssConstructorCount</name>\n            <ruleset>Code Size Rules</ruleset>\n        </rule>\n        <rule>\n            <name>NcssMethodCount</name>\n            <ruleset>Code Size Rules</ruleset>\n        </rule>\n        <rule>\n            <name>NcssTypeCount</name>\n            <ruleset>Code Size Rules</ruleset>\n        </rule>\n        <rule>\n            <name>NPathComplexity</name>\n            <ruleset>Code Size Rules</ruleset>\n        </rule>\n        <rule>\n            <name>TooManyFields</name>\n            <ruleset>Code Size Rules</ruleset>\n        </rule>\n        <rule>\n            <name>TooManyMethods</name>\n            <ruleset>Code Size Rules</ruleset>\n        </rule>\n        <rule>\n            <name>AssignmentInOperand</name>\n            <ruleset>Controversial Rules</ruleset>\n        </rule>\n        <rule>\n            <name>AtLeastOneConstructor</name>\n            <ruleset>Controversial Rules</ruleset>\n        </rule>\n        <rule>\n            <name>AvoidAccessibilityAlteration</name>\n            <ruleset>Controversial Rules</ruleset>\n        </rule>\n        <rule>\n            <name>AvoidUsingNativeCode</name>\n            <ruleset>Controversial Rules</ruleset>\n        </rule>\n        <rule>\n            <name>AvoidUsingShortType</name>\n            <ruleset>Controversial Rules</ruleset>\n        </rule>\n        <rule>\n            <name>AvoidUsingVolatile</name>\n            <ruleset>Controversial Rules</ruleset>\n        </rule>\n        <rule>\n            <name>BooleanInversion</name>\n            <ruleset>Controversial Rules</ruleset>\n        </rule>\n        <rule>\n            <name>CallSuperInConstructor</name>\n            <ruleset>Controversial Rules</ruleset>\n        </rule>\n        <rule>\n            <name>DefaultPackage</name>\n            <ruleset>Controversial Rules</ruleset>\n        </rule>\n        <rule>\n            <name>DoNotCallGarbageCollectionExplicitly</name>\n            <ruleset>Controversial Rules</ruleset>\n        </rule>\n        <rule>\n            <name>DontImportSun</name>\n            <ruleset>Controversial Rules</ruleset>\n        </rule>\n        <rule>\n            <name>SuspiciousOctalEscape</name>\n            <ruleset>Controversial Rules</ruleset>\n        </rule>\n        <rule>\n            <name>UnnecessaryConstructor</name>\n            <ruleset>Controversial Rules</ruleset>\n        </rule>\n        <rule>\n            <name>UnnecessaryParentheses</name>\n            <ruleset>Controversial Rules</ruleset>\n        </rule>\n        <rule>\n            <name>UnusedModifier</name>\n            <ruleset>Controversial Rules</ruleset>\n        </rule>\n        <rule>\n            <name>CouplingBetweenObjects</name>\n            <ruleset>Coupling Rules</ruleset>\n        </rule>\n        <rule>\n            <name>ExcessiveImports</name>\n            <ruleset>Coupling Rules</ruleset>\n        </rule>\n        <rule>\n            <name>AbstractClassWithoutAbstractMethod</name>\n            <ruleset>Design Rules</ruleset>\n        </rule>\n        <rule>\n            <name>AbstractClassWithoutAnyMethod</name>\n            <ruleset>Design Rules</ruleset>\n        </rule>\n        <rule>\n            <name>AccessorClassGeneration</name>\n            <ruleset>Design Rules</ruleset>\n        </rule>\n        <rule>\n            <name>AssignmentToNonFinalStatic</name>\n            <ruleset>Design Rules</ruleset>\n        </rule>\n        <rule>\n            <name>AvoidConstantsInterface</name>\n            <ruleset>Design Rules</ruleset>\n        </rule>\n        <rule>\n            <name>AvoidDeeplyNestedIfStmts</name>\n            <ruleset>Design Rules</ruleset>\n        </rule>\n        <rule>\n            <name>AvoidInstanceofChecksInCatchClause</name>\n            <ruleset>Design Rules</ruleset>\n        </rule>\n        <rule>\n            <name>AvoidProtectedFieldInFinalClass</name>\n            <ruleset>Design Rules</ruleset>\n        </rule>\n        <rule>\n            <name>AvoidReassigningParameters</name>\n            <ruleset>Design Rules</ruleset>\n        </rule>\n        <rule>\n            <name>AvoidSynchronizedAtMethodLevel</name>\n            <ruleset>Design Rules</ruleset>\n        </rule>\n        <rule>\n            <name>BadComparison</name>\n            <ruleset>Design Rules</ruleset>\n        </rule>\n        <rule>\n            <name>ClassWithOnlyPrivateConstructorsShouldBeFinal</name>\n            <ruleset>Design Rules</ruleset>\n        </rule>\n        <rule>\n            <name>CloseResource</name>\n            <ruleset>Design Rules</ruleset>\n        </rule>\n        <rule>\n            <name>CompareObjectsWithEquals</name>\n            <ruleset>Design Rules</ruleset>\n        </rule>\n        <rule>\n            <name>ConfusingTernary</name>\n            <ruleset>Design Rules</ruleset>\n        </rule>\n        <rule>\n            <name>ConstructorCallsOverridableMethod</name>\n            <ruleset>Design Rules</ruleset>\n        </rule>\n        <rule>\n            <name>DefaultLabelNotLastInSwitchStmt</name>\n            <ruleset>Design Rules</ruleset>\n        </rule>\n        <rule>\n            <name>EmptyMethodInAbstractClassShouldBeAbstract</name>\n            <ruleset>Design Rules</ruleset>\n        </rule>\n        <rule>\n            <name>EqualsNull</name>\n            <ruleset>Design Rules</ruleset>\n        </rule>\n        <rule>\n            <name>FinalFieldCouldBeStatic</name>\n            <ruleset>Design Rules</ruleset>\n        </rule>\n        <rule>\n            <name>IdempotentOperations</name>\n            <ruleset>Design Rules</ruleset>\n        </rule>\n        <rule>\n            <name>ImmutableField</name>\n            <ruleset>Design Rules</ruleset>\n        </rule>\n        <rule>\n            <name>InstantiationToGetClass</name>\n            <ruleset>Design Rules</ruleset>\n        </rule>\n        <rule>\n            <name>MissingBreakInSwitch</name>\n            <ruleset>Design Rules</ruleset>\n        </rule>\n        <rule>\n            <name>MissingStaticMethodInNonInstantiatableClass</name>\n            <ruleset>Design Rules</ruleset>\n        </rule>\n        <rule>\n            <name>NonCaseLabelInSwitchStatement</name>\n            <ruleset>Design Rules</ruleset>\n        </rule>\n        <rule>\n            <name>NonStaticInitializer</name>\n            <ruleset>Design Rules</ruleset>\n        </rule>\n        <rule>\n            <name>NonThreadSafeSingleton</name>\n            <ruleset>Design Rules</ruleset>\n        </rule>\n        <rule>\n            <name>OptimizableToArrayCall</name>\n            <ruleset>Design Rules</ruleset>\n        </rule>\n        <rule>\n            <name>PositionLiteralsFirstInComparisons</name>\n            <ruleset>Design Rules</ruleset>\n        </rule>\n        <rule>\n            <name>PreserveStackTrace</name>\n            <ruleset>Design Rules</ruleset>\n        </rule>\n        <rule>\n            <name>ReturnEmptyArrayRatherThanNull</name>\n            <ruleset>Design Rules</ruleset>\n        </rule>\n        <rule>\n            <name>SimpleDateFormatNeedsLocale</name>\n            <ruleset>Design Rules</ruleset>\n        </rule>\n        <rule>\n            <name>SimplifyBooleanExpressions</name>\n            <ruleset>Design Rules</ruleset>\n        </rule>\n        <rule>\n            <name>SimplifyBooleanReturns</name>\n            <ruleset>Design Rules</ruleset>\n        </rule>\n        <rule>\n            <name>SimplifyConditional</name>\n            <ruleset>Design Rules</ruleset>\n        </rule>\n        <rule>\n            <name>SingularField</name>\n            <ruleset>Design Rules</ruleset>\n        </rule>\n        <rule>\n            <name>SwitchDensity</name>\n            <ruleset>Design Rules</ruleset>\n        </rule>\n        <rule>\n            <name>SwitchStmtsShouldHaveDefault</name>\n            <ruleset>Design Rules</ruleset>\n        </rule>\n        <rule>\n            <name>TooFewBranchesForASwitchStatement</name>\n            <ruleset>Design Rules</ruleset>\n        </rule>\n        <rule>\n            <name>UncommentedEmptyConstructor</name>\n            <ruleset>Design Rules</ruleset>\n        </rule>\n        <rule>\n            <name>UncommentedEmptyMethod</name>\n            <ruleset>Design Rules</ruleset>\n        </rule>\n        <rule>\n            <name>UnnecessaryLocalBeforeReturn</name>\n            <ruleset>Design Rules</ruleset>\n        </rule>\n        <rule>\n            <name>UnsynchronizedStaticDateFormatter</name>\n            <ruleset>Design Rules</ruleset>\n        </rule>\n        <rule>\n            <name>UseCollectionIsEmpty</name>\n            <ruleset>Design Rules</ruleset>\n        </rule>\n        <rule>\n            <name>UseLocaleWithCaseConversions</name>\n            <ruleset>Design Rules</ruleset>\n        </rule>\n        <rule>\n            <name>UseNotifyAllInsteadOfNotify</name>\n            <ruleset>Design Rules</ruleset>\n        </rule>\n        <rule>\n            <name>UseSingleton</name>\n            <ruleset>Design Rules</ruleset>\n        </rule>\n        <rule>\n            <name>AvoidCallingFinalize</name>\n            <ruleset>Finalizer Rules</ruleset>\n        </rule>\n        <rule>\n            <name>EmptyFinalizer</name>\n            <ruleset>Finalizer Rules</ruleset>\n        </rule>\n        <rule>\n            <name>FinalizeDoesNotCallSuperFinalize</name>\n            <ruleset>Finalizer Rules</ruleset>\n        </rule>\n        <rule>\n            <name>FinalizeOnlyCallsSuperFinalize</name>\n            <ruleset>Finalizer Rules</ruleset>\n        </rule>\n        <rule>\n            <name>FinalizeOverloaded</name>\n            <ruleset>Finalizer Rules</ruleset>\n        </rule>\n        <rule>\n            <name>FinalizeShouldBeProtected</name>\n            <ruleset>Finalizer Rules</ruleset>\n        </rule>\n        <rule>\n            <name>DontImportJavaLang</name>\n            <ruleset>Import Statement Rules</ruleset>\n        </rule>\n        <rule>\n            <name>DuplicateImports</name>\n            <ruleset>Import Statement Rules</ruleset>\n        </rule>\n        <rule>\n            <name>ImportFromSamePackage</name>\n            <ruleset>Import Statement Rules</ruleset>\n        </rule>\n        <rule>\n            <name>TooManyStaticImports</name>\n            <ruleset>Import Statement Rules</ruleset>\n        </rule>\n        <rule>\n            <name>DoNotCallSystemExit</name>\n            <ruleset>J2EE Rules</ruleset>\n        </rule>\n        <rule>\n            <name>DoNotUseThreads</name>\n            <ruleset>J2EE Rules</ruleset>\n        </rule>\n        <rule>\n            <name>LocalHomeNamingConvention</name>\n            <ruleset>J2EE Rules</ruleset>\n        </rule>\n        <rule>\n            <name>LocalInterfaceSessionNamingConvention</name>\n            <ruleset>J2EE Rules</ruleset>\n        </rule>\n        <rule>\n            <name>MDBAndSessionBeanNamingConvention</name>\n            <ruleset>J2EE Rules</ruleset>\n        </rule>\n        <rule>\n            <name>RemoteInterfaceNamingConvention</name>\n            <ruleset>J2EE Rules</ruleset>\n        </rule>\n        <rule>\n            <name>RemoteSessionInterfaceNamingConvention</name>\n            <ruleset>J2EE Rules</ruleset>\n        </rule>\n        <rule>\n            <name>StaticEJBFieldShouldBeFinal</name>\n            <ruleset>J2EE Rules</ruleset>\n        </rule>\n        <rule>\n            <name>UseProperClassLoader</name>\n            <ruleset>J2EE Rules</ruleset>\n        </rule>\n        <rule>\n            <name>ProperLogger</name>\n            <ruleset>Jakarta Commons Logging Rules</ruleset>\n        </rule>\n        <rule>\n            <name>UseCorrectExceptionLogging</name>\n            <ruleset>Jakarta Commons Logging Rules</ruleset>\n        </rule>\n        <rule>\n            <name>AvoidPrintStackTrace</name>\n            <ruleset>Java Logging Rules</ruleset>\n        </rule>\n        <rule>\n            <name>LoggerIsNotStaticFinal</name>\n            <ruleset>Java Logging Rules</ruleset>\n        </rule>\n        <rule>\n            <name>MoreThanOneLogger</name>\n            <ruleset>Java Logging Rules</ruleset>\n        </rule>\n        <rule>\n            <name>SystemPrintln</name>\n            <ruleset>Java Logging Rules</ruleset>\n        </rule>\n        <rule>\n            <name>MissingSerialVersionUID</name>\n            <ruleset>JavaBean Rules</ruleset>\n        </rule>\n        <rule>\n            <name>JUnitAssertionsShouldIncludeMessage</name>\n            <ruleset>JUnit Rules</ruleset>\n        </rule>\n        <rule>\n            <name>JUnitSpelling</name>\n            <ruleset>JUnit Rules</ruleset>\n        </rule>\n        <rule>\n            <name>JUnitStaticSuite</name>\n            <ruleset>JUnit Rules</ruleset>\n        </rule>\n        <rule>\n            <name>JUnitTestsShouldIncludeAssert</name>\n            <ruleset>JUnit Rules</ruleset>\n        </rule>\n        <rule>\n            <name>SimplifyBooleanAssertion</name>\n            <ruleset>JUnit Rules</ruleset>\n        </rule>\n        <rule>\n            <name>TestClassWithoutTestCases</name>\n            <ruleset>JUnit Rules</ruleset>\n        </rule>\n        <rule>\n            <name>UnnecessaryBooleanAssertion</name>\n            <ruleset>JUnit Rules</ruleset>\n        </rule>\n        <rule>\n            <name>UseAssertEqualsInsteadOfAssertTrue</name>\n            <ruleset>JUnit Rules</ruleset>\n        </rule>\n        <rule>\n            <name>UseAssertNullInsteadOfAssertTrue</name>\n            <ruleset>JUnit Rules</ruleset>\n        </rule>\n        <rule>\n            <name>UseAssertSameInsteadOfAssertTrue</name>\n            <ruleset>JUnit Rules</ruleset>\n        </rule>\n        <rule>\n            <name>AvoidAssertAsIdentifier</name>\n            <ruleset>Migration Rules</ruleset>\n        </rule>\n        <rule>\n            <name>AvoidEnumAsIdentifier</name>\n            <ruleset>Migration Rules</ruleset>\n        </rule>\n        <rule>\n            <name>ByteInstantiation</name>\n            <ruleset>Migration Rules</ruleset>\n        </rule>\n        <rule>\n            <name>IntegerInstantiation</name>\n            <ruleset>Migration Rules</ruleset>\n        </rule>\n        <rule>\n            <name>JUnit4SuitesShouldUseSuiteAnnotation</name>\n            <ruleset>Migration Rules</ruleset>\n        </rule>\n        <rule>\n            <name>JUnit4TestShouldUseAfterAnnotation</name>\n            <ruleset>Migration Rules</ruleset>\n        </rule>\n        <rule>\n            <name>JUnit4TestShouldUseBeforeAnnotation</name>\n            <ruleset>Migration Rules</ruleset>\n        </rule>\n        <rule>\n            <name>JUnit4TestShouldUseTestAnnotation</name>\n            <ruleset>Migration Rules</ruleset>\n        </rule>\n        <rule>\n            <name>JUnitUseExpected</name>\n            <ruleset>Migration Rules</ruleset>\n        </rule>\n        <rule>\n            <name>LongInstantiation</name>\n            <ruleset>Migration Rules</ruleset>\n        </rule>\n        <rule>\n            <name>ReplaceEnumerationWithIterator</name>\n            <ruleset>Migration Rules</ruleset>\n        </rule>\n        <rule>\n            <name>ReplaceHashtableWithMap</name>\n            <ruleset>Migration Rules</ruleset>\n        </rule>\n        <rule>\n            <name>ReplaceVectorWithList</name>\n            <ruleset>Migration Rules</ruleset>\n        </rule>\n        <rule>\n            <name>ShortInstantiation</name>\n            <ruleset>Migration Rules</ruleset>\n        </rule>\n        <rule>\n            <name>AbstractNaming</name>\n            <ruleset>Naming Rules</ruleset>\n        </rule>\n        <rule>\n            <name>AvoidDollarSigns</name>\n            <ruleset>Naming Rules</ruleset>\n        </rule>\n        <rule>\n            <name>AvoidFieldNameMatchingMethodName</name>\n            <ruleset>Naming Rules</ruleset>\n        </rule>\n        <rule>\n            <name>AvoidFieldNameMatchingTypeName</name>\n            <ruleset>Naming Rules</ruleset>\n        </rule>\n        <rule>\n            <name>BooleanGetMethodName</name>\n            <ruleset>Naming Rules</ruleset>\n        </rule>\n        <rule>\n            <name>ClassNamingConventions</name>\n            <ruleset>Naming Rules</ruleset>\n        </rule>\n        <rule>\n            <name>LongVariable</name>\n            <ruleset>Naming Rules</ruleset>\n        </rule>\n        <rule>\n            <name>MethodNamingConventions</name>\n            <ruleset>Naming Rules</ruleset>\n        </rule>\n        <rule>\n            <name>MethodWithSameNameAsEnclosingClass</name>\n            <ruleset>Naming Rules</ruleset>\n        </rule>\n        <rule>\n            <name>MisleadingVariableName</name>\n            <ruleset>Naming Rules</ruleset>\n        </rule>\n        <rule>\n            <name>NoPackage</name>\n            <ruleset>Naming Rules</ruleset>\n        </rule>\n        <rule>\n            <name>PackageCase</name>\n            <ruleset>Naming Rules</ruleset>\n        </rule>\n        <rule>\n            <name>ShortMethodName</name>\n            <ruleset>Naming Rules</ruleset>\n        </rule>\n        <rule>\n            <name>ShortVariable</name>\n            <ruleset>Naming Rules</ruleset>\n        </rule>\n        <rule>\n            <name>SuspiciousConstantFieldName</name>\n            <ruleset>Naming Rules</ruleset>\n        </rule>\n        <rule>\n            <name>SuspiciousEqualsMethodName</name>\n            <ruleset>Naming Rules</ruleset>\n        </rule>\n        <rule>\n            <name>SuspiciousHashcodeMethodName</name>\n            <ruleset>Naming Rules</ruleset>\n        </rule>\n        <rule>\n            <name>VariableNamingConventions</name>\n            <ruleset>Naming Rules</ruleset>\n        </rule>\n        <rule>\n            <name>AddEmptyString</name>\n            <ruleset>Optimization Rules</ruleset>\n        </rule>\n        <rule>\n            <name>AvoidArrayLoops</name>\n            <ruleset>Optimization Rules</ruleset>\n        </rule>\n        <rule>\n            <name>AvoidInstantiatingObjectsInLoops</name>\n            <ruleset>Optimization Rules</ruleset>\n        </rule>\n        <rule>\n            <name>MethodArgumentCouldBeFinal</name>\n            <ruleset>Optimization Rules</ruleset>\n        </rule>\n        <rule>\n            <name>SimplifyStartsWith</name>\n            <ruleset>Optimization Rules</ruleset>\n        </rule>\n        <rule>\n            <name>UnnecessaryWrapperObjectCreation</name>\n            <ruleset>Optimization Rules</ruleset>\n        </rule>\n        <rule>\n            <name>UseArrayListInsteadOfVector</name>\n            <ruleset>Optimization Rules</ruleset>\n        </rule>\n        <rule>\n            <name>UseArraysAsList</name>\n            <ruleset>Optimization Rules</ruleset>\n        </rule>\n        <rule>\n            <name>UseStringBufferForStringAppends</name>\n            <ruleset>Optimization Rules</ruleset>\n        </rule>\n        <rule>\n            <name>ArrayIsStoredDirectly</name>\n            <ruleset>Security Code Guidelines</ruleset>\n        </rule>\n        <rule>\n            <name>MethodReturnsInternalArray</name>\n            <ruleset>Security Code Guidelines</ruleset>\n        </rule>\n        <rule>\n            <name>AvoidCatchingNPE</name>\n            <ruleset>Strict Exception Rules</ruleset>\n        </rule>\n        <rule>\n            <name>AvoidCatchingThrowable</name>\n            <ruleset>Strict Exception Rules</ruleset>\n        </rule>\n        <rule>\n            <name>AvoidRethrowingException</name>\n            <ruleset>Strict Exception Rules</ruleset>\n        </rule>\n        <rule>\n            <name>AvoidThrowingNewInstanceOfSameException</name>\n            <ruleset>Strict Exception Rules</ruleset>\n        </rule>\n        <rule>\n            <name>AvoidThrowingNullPointerException</name>\n            <ruleset>Strict Exception Rules</ruleset>\n        </rule>\n        <rule>\n            <name>AvoidThrowingRawExceptionTypes</name>\n            <ruleset>Strict Exception Rules</ruleset>\n        </rule>\n        <rule>\n            <name>DoNotExtendJavaLangError</name>\n            <ruleset>Strict Exception Rules</ruleset>\n        </rule>\n        <rule>\n            <name>DoNotThrowExceptionInFinally</name>\n            <ruleset>Strict Exception Rules</ruleset>\n        </rule>\n        <rule>\n            <name>ExceptionAsFlowControl</name>\n            <ruleset>Strict Exception Rules</ruleset>\n        </rule>\n        <rule>\n            <name>AppendCharacterWithChar</name>\n            <ruleset>String and StringBuffer Rules</ruleset>\n        </rule>\n        <rule>\n            <name>AvoidDuplicateLiterals</name>\n            <ruleset>String and StringBuffer Rules</ruleset>\n        </rule>\n        <rule>\n            <name>AvoidStringBufferField</name>\n            <ruleset>String and StringBuffer Rules</ruleset>\n        </rule>\n        <rule>\n            <name>ConsecutiveLiteralAppends</name>\n            <ruleset>String and StringBuffer Rules</ruleset>\n        </rule>\n        <rule>\n            <name>InefficientEmptyStringCheck</name>\n            <ruleset>String and StringBuffer Rules</ruleset>\n        </rule>\n        <rule>\n            <name>InefficientStringBuffering</name>\n            <ruleset>String and StringBuffer Rules</ruleset>\n        </rule>\n        <rule>\n            <name>InsufficientStringBufferDeclaration</name>\n            <ruleset>String and StringBuffer Rules</ruleset>\n        </rule>\n        <rule>\n            <name>StringBufferInstantiationWithChar</name>\n            <ruleset>String and StringBuffer Rules</ruleset>\n        </rule>\n        <rule>\n            <name>StringInstantiation</name>\n            <ruleset>String and StringBuffer Rules</ruleset>\n        </rule>\n        <rule>\n            <name>StringToString</name>\n            <ruleset>String and StringBuffer Rules</ruleset>\n        </rule>\n        <rule>\n            <name>UnnecessaryCaseChange</name>\n            <ruleset>String and StringBuffer Rules</ruleset>\n        </rule>\n        <rule>\n            <name>UseEqualsToCompareStrings</name>\n            <ruleset>String and StringBuffer Rules</ruleset>\n        </rule>\n        <rule>\n            <name>UseIndexOfChar</name>\n            <ruleset>String and StringBuffer Rules</ruleset>\n        </rule>\n        <rule>\n            <name>UselessStringValueOf</name>\n            <ruleset>String and StringBuffer Rules</ruleset>\n        </rule>\n        <rule>\n            <name>UseStringBufferLength</name>\n            <ruleset>String and StringBuffer Rules</ruleset>\n        </rule>\n        <rule>\n            <name>CloneMethodMustImplementCloneable</name>\n            <ruleset>Type Resolution Rules</ruleset>\n        </rule>\n        <rule>\n            <name>LooseCoupling</name>\n            <ruleset>Type Resolution Rules</ruleset>\n        </rule>\n        <rule>\n            <name>SignatureDeclareThrowsException</name>\n            <ruleset>Type Resolution Rules</ruleset>\n        </rule>\n        <rule>\n            <name>UnusedImports</name>\n            <ruleset>Type Resolution Rules</ruleset>\n        </rule>\n        <rule>\n            <name>UnusedFormalParameter</name>\n            <ruleset>Unused Code Rules</ruleset>\n        </rule>\n        <rule>\n            <name>UnusedLocalVariable</name>\n            <ruleset>Unused Code Rules</ruleset>\n        </rule>\n        <rule>\n            <name>UnusedPrivateField</name>\n            <ruleset>Unused Code Rules</ruleset>\n        </rule>\n        <rule>\n            <name>UnusedPrivateMethod</name>\n            <ruleset>Unused Code Rules</ruleset>\n        </rule>\n    </rules>\n    <includeDerivedFiles>false</includeDerivedFiles>\n    <violationsAsErrors>true</violationsAsErrors>\n</pmd>\n"
  },
  {
    "path": ".travis.yml__",
    "content": "#\n# Apache HTTPD & NGINX Access log parsing made easy\n# Copyright (C) 2011-2023 Niels Basjes\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\nlanguage: java\n\ndist: trusty\n\njdk:\n  - oraclejdk8\n\nafter_success:\n  - mvn -PEnableReportPlugins coveralls:report\n\ncache:\n  directories:\n    - '$HOME/.m2/repository'\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "This is intended as an overview of the major changes\n\nv6.0.1-SNAPSHOT\n===\n- ...\n\nv6.0.0\n===\n- Updated to need Java 21!\n- Update Serde to Hive 4.2.0\n- Updated Flink and Beam examples to latest versions\n- GeoIP update no longer supports the \"metrocode\"\n\nv5.11.0\n===\n- Update Serde to Hive 4.0.0\n\nv5.10.0\n===\n- Do not fail on logline specific parse errors, simply not provide the expected dissections.\n\nv5.9.0\n===\n- Print the parsed values of a wildcard in DissectorTester::printAllPossibleValues\n- Allow bad requests to be parsed anyway.\n- Drop Apache Pig support (this tool is no longer used by anyone)\n\nv5.8\n===\n- getPossiblePaths sorts the list by fieldname.\n- Removed GeoIP fields averageincome and populationdensity which are not part of any real mmdb file.\n- Dropped the already disabled Storm example\n- Fully switched to Junit 5\n- Require JDK 11 or newer to build\n- Workaround for change in Unicode CLDR(and thus Java 17): they changed the short name of \"September\" in Locale.UK to \"Sept\" which causes parse errors.\n- Fixed bug regarding escaped characters in headers.\n\nv5.7\n===\n- Updated dependencies\n- When adding a type remapping with an explicit cast this cast was lost and replaced by \"STRING_ONLY\".\n\nv5.6\n===\n- Fix bug that in some cases values would be reported multiple times.\n\nv5.5\n===\n- Handle HTML encoded values in the URL better\n\nv5.4\n===\n- Updated many dependencies\n- Fixed extracting the timezone\n- In url parsing missing values are now 'absent' (i.e. not set)\n- Handle loglines when the upstream module is not running (i.e. fields are a '-')\n\nv5.3\n===\n- Updated many dependencies\n\nv5.2\n===\n- Improve regex performance\n- Added basic support for the NGinx Upstream, SSL, GeoIP and all other documented modules\n- Added basic support for parsing the Kubernetes Ingress logformat variables\n- Disallow some Dissector methods to return a null.\n- Updated many dependencies: Hadoop, Flink, Beam etc.\n\nv5.1\n===\n- Parse epoch seconds `%{%s}t`\n- Added GeoIP2 dissectors for City, Country and ASN data.\n- Improved output of dissector testing framework.\n\nv5.0\n===\n- The %u specifier allowed a space which broke parsing if the field after it also allowed space.\n- If a custom time format does not contain a timezone we assume a default timezone (UTC).\n- Fix extracting milliseconds, added extracting microseconds and nanoseconds.\n- Allow `%{%usec_frac}t` in addition to `%{usec_frac}t` (same msec_frac).\n- Introduce a setterPolicy to determine if the setter is called in case of a NULL or EMPTY value.\n- Replace Cobertura with Jacoco because of Java 8\n- Remove Yauaa from tests and examples because of circular dependency between the projects.\n- Make Java API more fluent (breaks backwards compatibility with external dissectors).\n\nv4.0\n===\n- Switching to require Java 8\n- Parser instance is now serializable.\n- Added example on using with Apache Flink\n- Added example on using with Apache Beam\n- Rewrote Apache Storm code and move to examples\n- Many changes in Exception handling\n\nv3.1\n===\n- Handle illegal data: Double # in the URL\n- Handle illegal data: Firstline is rubbish (Reported by Yong Zhang - java8964).\n\nv3.0\n===\n- Accept the NGinx logformat.\n- Output the httpd parser git info, build timestamp and version info on startup.\n- Fixed problem when using a different root dissector than provided by default.\n- Created a testing toolkit for dissectors and improved several tests.\n- Allow a dissector to add an additional dissector (needed for custom time format parsing)\n- Support for parsing many (not all) of the possible custom time format fields.\n- Allow changing the parser even after first use (makes it more flexible to use).\n- Allow a dissector to return an empty 'extra part' to allow an alternate form of type remapping\n- Improved test coverage\n- Dissection the setcookies expire value was not deterministic and an absent 'expire' value is now returned as null instead of the 'now' timestamp.\n- Implemented `%{UNIT}T`\n- Implemented converters between several closely related formats (BYTES/BYTESCLF, time related formats)\n- Token based parsers can output multiple values for the same parameter.\n- Implemented all < and > directives for Apache logformat\n- Implemented `%{VARNAME}^ti` and `%{VARNAME}^to`\n\n**RELEVANT CHANGES COMPARED WITH THE 2.X VERSION**:\nThese fields are now reported as deprecated.\n- `%b` changed from \"BYTES:response.body.bytesclf\" to \"BYTESCLF:response.body.bytes\"\n- `%D` changed from \"server.process.time\" to \"response.server.processing.time\"\n- `%{msec_frac}t` changed from \"request.receive.time.begin.msec_frac\" to \"request.receive.time.msec_frac\"\n- `%{usec_frac}t` changed from \"request.receive.time.begin.usec_frac\" to \"request.receive.time.usec_frac\"\n- `%{msec}t`      changed from \"request.receive.time.begin.msec\"      to \"request.receive.time.msec\"\n- `%{usec}t`      changed from \"request.receive.time.begin.usec\"      to \"request.receive.time.usec\"\n\nv2.8\n===\n- Allow parsing mixed case timeformats\n- Added the ISO 8601 'date' output for parsed times (Is string \"yyyy-MM-dd\")\n- Added the ISO 8601 'time' output for parsed times (Is string \"HH:mm:ss\"  )\n- Solve parse error when a HTTP method like \"VERSION-CONTROL\" is used.\n- Fixed NPE in specific combination of cast and setter type and a null value\n- Improved test coverage\n\nv2.7\n===\n- Handle the effects of mod_reqtimeout giving a http 408 (reported by Diogo Sant'Ana)\n- Added option to optionally continue even when some requested dissectors are missing (Java only).\n\nv2.6\n===\n- Buildin fix for the problems in Jetty logging\n\nv2.5\n===\n- Simply treat the `%{...}t` as a text field you can retrieve (instead of failing if it occurs).\n- Change URI dissector to allow URIs like android-app://...\n- Change URI dissector to allow % in the URI when it is not an escape sequence (like in `?promo=Give-5%-discount`)\n\nv2.4\n===\n- Rewrote the way parsed values are passed around. Improves accuracy and performance in specific cases.\n- Now support parsing the first line even if it is chopped by Apache httpd because of an URI longer than 8000 bytes.\n- Fixed an infinite recursion problem.\n- Fixed Timestamp unit test (test was broken, code was fine).\n\nv2.3\n===\n- The raw timestamp extracted from the Apache logfiles no longer contains the surrounding '[' ']'.\n\nv2.2\n===\n- Accept multiple logformat lines as a single 'multiline' input string.\n\nv2.1.1\n===\n- Fixed simple problem in the PIG example output\n\nv2.1\n===\n- Dissect the unique ID from mod_unique_id.\n- [PIG] Make getting the example code for PIG a bit easier\n\nv2.0\n===\n- Fixed reading logfiles from before 2000\n- Rearranged the Java packages to make the structure more logical.\n- Changed license from GPL to Apache v2.0\n\nv1.9.1\n===\n- Allow urls that have a space (which is incorrect but does happen).\n\nv1.9\n===\n- [PIG] Support for getting a map[] of values (useful for cookies and query string parameters)\n- [PIG] Output the possible values as a complete working Pig statement.\n\nOlder\n===\nJust see the commit logs\n\n\n    Apache HTTPD & NGINX Access log parsing made easy\n    Copyright (C) 2011-2023 Niels Basjes\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n    https://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n"
  },
  {
    "path": "CNAME",
    "content": "logparser.basjes.nl"
  },
  {
    "path": "GeoIP2-TestData/Dockerfile",
    "content": "# Apache HTTPD & NGINX Access log parsing made easy\n# Copyright (C) 2011-2023 Niels Basjes\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nFROM perl:5.40.0-buster\n\nRUN cpan File::Slurper\nRUN cpan Cpanel::JSON::XS\nRUN cpan Math::Int128\nRUN cpan MaxMind::DB::Writer::Serializer\n\nWORKDIR \"/GeoIPTestData/test-data\"\n\nCMD [\"/GeoIPTestData/test-data/write-test-data.pl\"]\n"
  },
  {
    "path": "GeoIP2-TestData/README.md",
    "content": "I copied some of the files from https://github.com/maxmind/MaxMind-DB/\nand modified them to suit my own needs.\n\nThese base files are only used for unit tests in this project and are licensed as\n\n    This work is licensed under the Creative Commons Attribution-ShareAlike 3.0\n    Unported License. To view a copy of this license, visit\n    http://creativecommons.org/licenses/by-sa/3.0/ or send a letter to Creative\n    Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA.\n"
  },
  {
    "path": "GeoIP2-TestData/rebuild.sh",
    "content": "#!/bin/bash\n# Apache HTTPD & NGINX Access log parsing made easy\n# Copyright (C) 2011-2023 Niels Basjes\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nDIR=\"$( cd \"$( dirname \"${BASH_SOURCE[0]}\" )\" >/dev/null && pwd )\"\n\ncd \"${DIR}\" || exit 1\n\nPROJECTNAME=GeoIPTestData\nIMAGE_NAME=geo-ip-testdatabuilder\nCONTAINER_NAME=GeoIPTestDataBuilder\n\ndocker build -t \"${IMAGE_NAME}\" .\n\nif [ \"$(uname -s)\" == \"Linux\" ]; then\n  USER_NAME=${SUDO_USER:=${USER}}\n  USER_ID=$(id -u \"${USER_NAME}\")\n  GROUP_ID=$(id -g \"${USER_NAME}\")\nelse # boot2docker uid and gid\n  USER_NAME=${USER}\n  USER_ID=1000\n  GROUP_ID=50\nfi\n\n# man docker-run\n# When using SELinux, mounted directories may not be accessible\n# to the container. To work around this, with Docker prior to 1.7\n# one needs to run the \"chcon -Rt svirt_sandbox_file_t\" command on\n# the directories. With Docker 1.7 and later the z mount option\n# does this automatically.\n# Since Docker 1.7 was release 5 years ago we only support 1.7 and newer.\nV_OPTS=:z\n\nCOMMAND=( \"$@\" )\nif [ $# -eq 0 ];\nthen\n#  COMMAND=( \"bash\" \"-i\" )\n  COMMAND=( )\nfi\n\n( cd \"${DIR}/test-data\" && rm *.mmdb )\n\ndocker run --rm=true -i -t                    \\\n       -u \"${USER_ID}\"                        \\\n       -v \"${PWD}:/GeoIPTestData${V_OPTS:-}\"  \\\n       -w \"/GeoIPTestData/test-data\"          \\\n       --name \"${CONTAINER_NAME}\"             \\\n       \"${IMAGE_NAME}\"                        \\\n       \"${COMMAND[@]}\"\n"
  },
  {
    "path": "GeoIP2-TestData/source-data/GeoIP2-City-Test.json",
    "content": "[\n  {\n    \"::80.100.47.45/118\": {\n      \"city\": {\n        \"confidence\": 1,\n        \"geoname_id\": \"1234\",\n        \"names\": {\n          \"en\": \"Amstelveen\"\n        }\n      },\n      \"postal\": {\n        \"code\": \"1187\",\n        \"confidence\": 2\n      },\n      \"subdivisions\": [\n        {\n          \"confidence\": 3,\n          \"geoname_id\": 2345,\n          \"iso_code\": \"NH\",\n          \"names\": {\n            \"en\": \"Noord Holland\"\n          }\n        }\n      ],\n      \"continent\": {\n        \"code\": \"EU\",\n        \"geoname_id\": 6255148,\n        \"names\": {\n          \"de\": \"Europa\",\n          \"en\": \"Europe\",\n          \"es\": \"Europa\",\n          \"fr\": \"Europe\",\n          \"ja\": \"ヨーロッパ\",\n          \"pt-BR\": \"Europa\",\n          \"ru\": \"Европа\",\n          \"zh-CN\": \"欧洲\"\n        }\n      },\n      \"country\": {\n        \"geoname_id\": 2750405,\n        \"is_in_european_union\": true,\n        \"iso_code\": \"NL\",\n        \"confidence\": 42,\n        \"names\": {\n          \"de\": \"Niederlande\",\n          \"en\": \"Netherlands\",\n          \"es\": \"Holanda\",\n          \"fr\": \"Pays-Bas\",\n          \"ja\": \"オランダ王国\",\n          \"pt-BR\": \"Países Baixos\",\n          \"ru\": \"Нидерланды\",\n          \"zh-CN\": \"荷兰\"\n        }\n      },\n      \"location\": {\n        \"accuracy_radius\": 4,\n        \"latitude\": \"52.5\",\n        \"longitude\": \"5.75\",\n        \"time_zone\": \"Europe/Amsterdam\",\n        \"metro_code\" : \"5\"\n      },\n      \"registered_country\": {\n        \"geoname_id\": 2750405,\n        \"is_in_european_union\": true,\n        \"iso_code\": \"NL\",\n        \"names\": {\n          \"de\": \"Niederlande\",\n          \"en\": \"Netherlands\",\n          \"es\": \"Holanda\",\n          \"fr\": \"Pays-Bas\",\n          \"ja\": \"オランダ王国\",\n          \"pt-BR\": \"Países Baixos\",\n          \"ru\": \"Нидерланды\",\n          \"zh-CN\": \"荷兰\"\n        }\n      }\n    }\n  },\n  {\n    \"2001:980:91c0::/56\": {\n      \"city\": {\n        \"confidence\": 11,\n        \"geoname_id\": \"1234\",\n        \"names\": {\n          \"en\": \"Amstelveen\"\n        }\n      },\n      \"postal\": {\n        \"code\": \"1187\",\n        \"confidence\": 12\n      },\n      \"subdivisions\": [\n        {\n          \"confidence\": 13,\n          \"geoname_id\": 2345,\n          \"iso_code\": \"NH\",\n          \"names\": {\n            \"en\": \"Noord Holland\"\n          }\n        }\n      ],\n      \"continent\": {\n        \"code\": \"EU\",\n        \"geoname_id\": 6255148,\n        \"names\": {\n          \"de\": \"Europa\",\n          \"en\": \"Europe\",\n          \"es\": \"Europa\",\n          \"fr\": \"Europe\",\n          \"ja\": \"ヨーロッパ\",\n          \"pt-BR\": \"Europa\",\n          \"ru\": \"Европа\",\n          \"zh-CN\": \"欧洲\"\n        }\n      },\n      \"country\": {\n        \"geoname_id\": 2750405,\n        \"is_in_european_union\": true,\n        \"iso_code\": \"NL\",\n        \"confidence\": 42,\n        \"names\": {\n          \"de\": \"Niederlande\",\n          \"en\": \"Netherlands\",\n          \"es\": \"Holanda\",\n          \"fr\": \"Pays-Bas\",\n          \"ja\": \"オランダ王国\",\n          \"pt-BR\": \"Países Baixos\",\n          \"ru\": \"Нидерланды\",\n          \"zh-CN\": \"荷兰\"\n        }\n      },\n      \"location\": {\n        \"accuracy_radius\": 14,\n        \"latitude\": \"52.5\",\n        \"longitude\": \"5.75\",\n        \"time_zone\": \"Europe/Amsterdam\",\n        \"metro_code\": \"15\"\n      },\n      \"registered_country\": {\n        \"geoname_id\": 2750405,\n        \"is_in_european_union\": true,\n        \"iso_code\": \"NL\",\n        \"names\": {\n          \"de\": \"Niederlande\",\n          \"en\": \"Netherlands\",\n          \"es\": \"Holanda\",\n          \"fr\": \"Pays-Bas\",\n          \"ja\": \"オランダ王国\",\n          \"pt-BR\": \"Países Baixos\",\n          \"ru\": \"Нидерланды\",\n          \"zh-CN\": \"荷兰\"\n        }\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "GeoIP2-TestData/source-data/GeoIP2-Country-Test.json",
    "content": "[\n  {\n    \"::80.100.47.45/118\": {\n      \"continent\": {\n        \"code\": \"EU\",\n        \"geoname_id\": 6255148,\n        \"names\": {\n          \"de\": \"Europa\",\n          \"en\": \"Europe\",\n          \"es\": \"Europa\",\n          \"fr\": \"Europe\",\n          \"ja\": \"ヨーロッパ\",\n          \"pt-BR\": \"Europa\",\n          \"ru\": \"Европа\",\n          \"zh-CN\": \"欧洲\"\n        }\n      },\n      \"country\": {\n        \"geoname_id\": 2750405,\n        \"is_in_european_union\": true,\n        \"iso_code\": \"NL\",\n        \"confidence\": 42,\n        \"names\": {\n          \"de\": \"Niederlande\",\n          \"en\": \"Netherlands\",\n          \"es\": \"Holanda\",\n          \"fr\": \"Pays-Bas\",\n          \"ja\": \"オランダ王国\",\n          \"pt-BR\": \"Países Baixos\",\n          \"ru\": \"Нидерланды\",\n          \"zh-CN\": \"荷兰\"\n        }\n      },\n      \"registered_country\": {\n        \"geoname_id\": 2750405,\n        \"is_in_european_union\": true,\n        \"iso_code\": \"NL\",\n        \"names\": {\n          \"de\": \"Niederlande\",\n          \"en\": \"Netherlands\",\n          \"es\": \"Holanda\",\n          \"fr\": \"Pays-Bas\",\n          \"ja\": \"オランダ王国\",\n          \"pt-BR\": \"Países Baixos\",\n          \"ru\": \"Нидерланды\",\n          \"zh-CN\": \"荷兰\"\n        }\n      }\n    }\n  },\n  {\n    \"2001:980:91c0::/56\": {\n      \"continent\": {\n        \"code\": \"EU\",\n        \"geoname_id\": 6255148,\n        \"names\": {\n          \"de\": \"Europa\",\n          \"en\": \"Europe\",\n          \"es\": \"Europa\",\n          \"fr\": \"Europe\",\n          \"ja\": \"ヨーロッパ\",\n          \"pt-BR\": \"Europa\",\n          \"ru\": \"Европа\",\n          \"zh-CN\": \"欧洲\"\n        }\n      },\n      \"country\": {\n        \"geoname_id\": 2750405,\n        \"is_in_european_union\": true,\n        \"iso_code\": \"NL\",\n        \"confidence\": 42,\n        \"names\": {\n          \"de\": \"Niederlande\",\n          \"en\": \"Netherlands\",\n          \"es\": \"Holanda\",\n          \"fr\": \"Pays-Bas\",\n          \"ja\": \"オランダ王国\",\n          \"pt-BR\": \"Países Baixos\",\n          \"ru\": \"Нидерланды\",\n          \"zh-CN\": \"荷兰\"\n        }\n      },\n      \"registered_country\": {\n        \"geoname_id\": 2750405,\n        \"is_in_european_union\": true,\n        \"iso_code\": \"NL\",\n        \"names\": {\n          \"de\": \"Niederlande\",\n          \"en\": \"Netherlands\",\n          \"es\": \"Holanda\",\n          \"fr\": \"Pays-Bas\",\n          \"ja\": \"オランダ王国\",\n          \"pt-BR\": \"Países Baixos\",\n          \"ru\": \"Нидерланды\",\n          \"zh-CN\": \"荷兰\"\n        }\n      }\n    }\n  }\n]\n"
  },
  {
    "path": "GeoIP2-TestData/source-data/GeoIP2-ISP-Test.json",
    "content": "[\n  {\n    \"::80.100.47.45/118\": {\n      \"autonomous_system_number\": 4444,\n      \"autonomous_system_organization\": \"Basjes Global Network\",\n      \"isp\": \"Basjes ISP\",\n      \"organization\": \"Niels Basjes\"\n    }\n  },\n  {\n    \"2001:980:91c0::/56\": {\n      \"autonomous_system_number\": 6666,\n      \"autonomous_system_organization\": \"Basjes Global Network IPv6\",\n      \"isp\": \"Basjes ISP IPv6\",\n      \"organization\": \"Niels Basjes IPv6\"\n    }\n  }\n]\n"
  },
  {
    "path": "GeoIP2-TestData/source-data/GeoLite2-ASN-Test.json",
    "content": "[\n  {\n    \"::80.100.47.45/118\": {\n      \"autonomous_system_number\": 4444,\n      \"autonomous_system_organization\": \"Basjes Global Network\"\n    }\n  },\n  {\n    \"2001:980:91c0::/56\": {\n      \"autonomous_system_number\": 6666,\n      \"autonomous_system_organization\": \"Basjes Global Network IPv6\"\n    }\n  }\n]\n"
  },
  {
    "path": "GeoIP2-TestData/test-data/write-test-data.pl",
    "content": "#!/usr/bin/env perl\n\nuse strict;\nuse warnings;\nuse autodie;\nuse utf8;\n\nuse Cwd qw( abs_path );\nuse File::Basename qw( dirname );\nuse File::Slurper qw( read_binary write_binary );\nuse Cpanel::JSON::XS 4.16 qw( decode_json );\nuse Math::Int128 qw( MAX_UINT128 string_to_uint128 uint128 );\nuse MaxMind::DB::Writer::Serializer 0.100004;\nuse MaxMind::DB::Writer::Tree 0.100004;\nuse MaxMind::DB::Writer::Util qw( key_for_data );\nuse Net::Works::Network ();\nuse Test::MaxMind::DB::Common::Util qw( standard_test_metadata );\n\nmy $Dir = dirname( abs_path($0) );\n\nsub main {\n#    my @sizes = ( 24, 28, 32 );\n#    my @ipv4_range = ( '1.1.1.1', '1.1.1.32' );\n\n#    my @ipv4_subnets = Net::Works::Network->range_as_subnets(@ipv4_range);\n#    for my $record_size (@sizes) {\n#        write_test_db(\n#            $record_size,\n#            \\@ipv4_subnets,\n#            { ip_version => 4 },\n#            'ipv4',\n#        );\n#    }\n\n#    write_broken_pointers_test_db(\n#        24,\n#        \\@ipv4_subnets,\n#        { ip_version => 4 },\n#        'broken-pointers',\n#    );\n\n#    write_broken_search_tree_db(\n#        24,\n#        \\@ipv4_subnets,\n#        { ip_version => 4 },\n#        'broken-search-tree',\n#    );\n\n#    my @ipv6_subnets = Net::Works::Network->range_as_subnets(\n#        '::1:ffff:ffff',\n#        '::2:0000:0059'\n#    );\n\n#    for my $record_size (@sizes) {\n#        write_test_db(\n#            $record_size,\n#            \\@ipv6_subnets,\n#            { ip_version => 6 },\n#            'ipv6',\n#        );\n#\n#        write_test_db(\n#            $record_size,\n#            [\n#                @ipv6_subnets,\n#                Net::Works::Network->range_as_subnets( @ipv4_range, 6 ),\n#            ],\n#            { ip_version => 6 },\n#            'mixed',\n#        );\n#    }\n\n#    write_decoder_test_db();\n#    write_pointer_decoder_test_db();\n#    write_deeply_nested_structures_db();\n\n    write_geoip2_dbs();\n#    write_broken_geoip2_city_db();\n#    write_invalid_node_count();\n\n#    write_no_ipv4_tree_db();\n\n#    write_no_map_db( \\@ipv4_subnets );\n\n#    write_test_serialization_data();\n\n#    write_db_with_metadata_pointers();\n}\n\n# sub write_broken_pointers_test_db {\n#     no warnings 'redefine';\n#\n#     my $orig_store_data = MaxMind::DB::Writer::Serializer->can('store_data');\n#\n#     # This breaks the value of the record for the 1.1.1.32 network, causing it\n#     # to point outside the database.\n#     local *MaxMind::DB::Writer::Serializer::store_data = sub {\n#         my $data_pointer = shift->$orig_store_data(@_);\n#         my $value        = $_[1];\n#         if (   ref($value) eq 'HASH'\n#             && exists $value->{ip}\n#             && $value->{ip} eq '1.1.1.32' ) {\n#\n#             $data_pointer += 100_000;\n#         }\n#         return $data_pointer;\n#     };\n#\n#     # The next hack will poison the data section for the 1.1.16/28 subnet\n#     # value. It's value will be a pointer that resolves to an offset outside\n#     # the database.\n#\n#     my $key_to_poison = key_for_data( { ip => '1.1.1.16' } );\n#\n#     my $orig_position_for_data\n#         = MaxMind::DB::Writer::Serializer->can('_position_for_data');\n#     local *MaxMind::DB::Writer::Serializer::_position_for_data = sub {\n#         my $key = $_[1];\n#\n#         if ( $key eq $key_to_poison ) {\n#             return 1_000_000;\n#         }\n#         else {\n#             return shift->$orig_position_for_data(@_);\n#         }\n#     };\n#\n#     write_test_db(@_);\n#\n#     return;\n# }\n\n# sub write_broken_search_tree_db {\n#     my $filename = ( write_test_db(@_) )[1];\n#\n#     my $content = read_binary($filename);\n#\n#     # This causes the right record of the first node to be 0, meaning it\n#     # points back to the top of the tree. This should never happen in a\n#     # database that follows the spec.\n#     substr( $content, 5, 1 ) = \"\\0\";\n#     write_binary( $filename, $content );\n#\n#     return;\n# }\n\n# sub write_test_db {\n#     my $record_size     = shift;\n#     my $subnets         = shift;\n#     my $metadata        = shift;\n#     my $ip_version_name = shift;\n#\n#     my $writer = MaxMind::DB::Writer::Tree->new(\n#         ip_version            => $subnets->[0]->version(),\n#         record_size           => $record_size,\n#         alias_ipv6_to_ipv4    => ( $subnets->[0]->version() == 6 ? 1 : 0 ),\n#         map_key_type_callback => sub { 'utf8_string' },\n#         standard_test_metadata(),\n#         %{$metadata},\n#     );\n#\n#     for my $subnet ( @{$subnets} ) {\n#         $writer->insert_network(\n#             $subnet,\n#             { ip => $subnet->first()->as_string() }\n#         );\n#     }\n#\n#     my $filename = sprintf(\n#         \"$Dir/MaxMind-DB-test-%s-%i.mmdb\",\n#         $ip_version_name,\n#         $record_size,\n#     );\n#     open my $fh, '>', $filename;\n#\n#     $writer->write_tree($fh);\n#\n#     close $fh;\n#\n#     return ( $writer, $filename );\n# }\n\n# {\n#     # We will store this once for each subnet so we will also be testing\n#     # pointers, since the serializer will generate a pointer to this\n#     # structure.\n#     my %all_types = (\n#         utf8_string => 'unicode! ☯ - ♫',\n#         double      => 42.123456,\n#         bytes       => pack( 'N', 42 ),\n#         uint16      => 100,\n#         uint32      => 2**28,\n#         int32       => -1 * ( 2**28 ),\n#         uint64      => uint128(1) << 60,\n#         uint128     => uint128(1) << 120,\n#         array       => [ 1, 2, 3, ],\n#         map         => {\n#             mapX => {\n#                 utf8_stringX => 'hello',\n#                 arrayX       => [ 7, 8, 9 ],\n#             },\n#         },\n#         boolean => 1,\n#         float   => 1.1,\n#     );\n#\n#     my %all_types_0 = (\n#         utf8_string => q{},\n#         double      => 0,\n#         bytes       => q{},\n#         uint16      => 0,\n#         uint32      => 0,\n#         int32       => 0,\n#         uint64      => uint128(0),\n#         uint128     => uint128(0),\n#         array       => [],\n#         map         => {},\n#         boolean     => 0,\n#         float       => 0,\n#     );\n#\n#     # We limit this to numeric types as the other types would generate\n#     # very large databases\n#     my %numeric_types_max = (\n#         double  => 'Inf',\n#         float   => 'Inf',\n#         int32   => 0x7fffffff,\n#         uint16  => 0xffff,\n#         uint32  => string_to_uint128('0xffff_ffff'),\n#         uint64  => string_to_uint128('0xffff_ffff_ffff_ffff'),\n#         uint128 => MAX_UINT128,\n#     );\n#\n#     sub write_decoder_test_db {\n#         my $writer = _decoder_writer();\n#\n#         my @subnets\n#             = map { Net::Works::Network->new_from_string( string => $_ ) }\n#             qw(\n#             ::1.1.1.0/120\n#             ::2.2.0.0/112\n#             ::3.0.0.0/104\n#             ::4.5.6.7/128\n#             abcd::/64\n#             1000::1234:0000/112\n#         );\n#\n#         for my $subnet (@subnets) {\n#             $writer->insert_network(\n#                 $subnet,\n#                 \\%all_types,\n#             );\n#         }\n#\n#         $writer->insert_network(\n#             Net::Works::Network->new_from_string( string => '::0.0.0.0/128' ),\n#             \\%all_types_0,\n#         );\n#\n#         $writer->insert_network(\n#             Net::Works::Network->new_from_string(\n#                 string => '::255.255.255.255/128'\n#             ),\n#             \\%numeric_types_max,\n#         );\n#\n#         open my $fh, '>', \"$Dir/MaxMind-DB-test-decoder.mmdb\";\n#         $writer->write_tree($fh);\n#         close $fh;\n#\n#         return;\n#     }\n#\n#     sub write_pointer_decoder_test_db {\n#\n#         # We want to create a database where most values are pointers\n#         no warnings 'redefine';\n#         local *MaxMind::DB::Writer::Serializer::_should_cache_value\n#             = sub { 1 };\n#         my $writer = _decoder_writer();\n#\n#         # We add these slightly different records so that we end up with\n#         # pointers for the individual values in the maps, not just pointers\n#         # to the map\n#         $writer->insert_network(\n#             '1.0.0.0/32',\n#             {\n#                 %all_types,\n#                 booleanX => 0,\n#                 arrayX   => [ 1, 2, 3, 4, ],\n#                 mapXX    => {\n#                     utf8_stringX => 'hello',\n#                     arrayX       => [ 7, 8, 9, 10 ],\n#                     booleanX     => 0,\n#                 },\n#             },\n#         );\n#\n#         $writer->insert_network(\n#             '1.1.1.0/32',\n#             {\n#                 %all_types,\n#\n#                 # This has to be 0 rather than 1 as otherwise the buggy\n#                 # Perl writer will think it is the same as an uint32 value of\n#                 # 1 and make a pointer to a value of a different type.\n#                 boolean => 0,\n#             },\n#         );\n#\n#         open my $fh, '>', \"$Dir/MaxMind-DB-test-pointer-decoder.mmdb\";\n#         $writer->write_tree($fh);\n#         close $fh;\n#\n#         return;\n#     }\n#\n#     sub _decoder_writer {\n#         return MaxMind::DB::Writer::Tree->new(\n#             ip_version    => 6,\n#             record_size   => 24,\n#             database_type => 'MaxMind DB Decoder Test',\n#             languages     => ['en'],\n#             description   => {\n#                 en =>\n#                     'MaxMind DB Decoder Test database - contains every MaxMind DB data type',\n#             },\n#             alias_ipv6_to_ipv4       => 1,\n#             remove_reserved_networks => 0,\n#             map_key_type_callback    => sub {\n#                 my $key = $_[0];\n#                 $key =~ s/X*$//;\n#                 return $key eq 'array' ? [ 'array', 'uint32' ] : $key;\n#             },\n#         );\n#     }\n# }\n\n# {\n#     my %nested = (\n#         map1 => {\n#             map2 => {\n#                 array => [\n#                     {\n#                         map3 => { a => 1, b => 2, c => 3 },\n#                     },\n#                 ],\n#             },\n#         },\n#     );\n#\n#     sub write_deeply_nested_structures_db {\n#         my $writer = MaxMind::DB::Writer::Tree->new(\n#             ip_version    => 6,\n#             record_size   => 24,\n#             ip_version    => 6,\n#             database_type => 'MaxMind DB Nested Data Structures',\n#             languages     => ['en'],\n#             description   => {\n#                 en =>\n#                     'MaxMind DB Nested Data Structures Test database - contains deeply nested map/array structures',\n#             },\n#             alias_ipv6_to_ipv4    => 1,\n#             map_key_type_callback => sub {\n#                 my $key = shift;\n#                 return\n#                       $key =~ /^map/  ? 'map'\n#                     : $key eq 'array' ? [ 'array', 'map' ]\n#                     :                   'uint32';\n#             }\n#         );\n#\n#         my @subnets\n#             = map { Net::Works::Network->new_from_string( string => $_ ) }\n#             qw(\n#             ::1.1.1.0/120\n#             ::2.2.0.0/112\n#             ::3.0.0.0/104\n#             ::4.5.6.7/128\n#             abcd::/64\n#             1000::1234:0000/112\n#         );\n#\n#         for my $subnet (@subnets) {\n#             $writer->insert_network(\n#                 $subnet,\n#                 \\%nested,\n#             );\n#         }\n#\n#         open my $fh, '>', \"$Dir/MaxMind-DB-test-nested.mmdb\";\n#         $writer->write_tree($fh);\n#         close $fh;\n#\n#         return;\n#     }\n# }\n\nsub write_geoip2_dbs {\n    _write_geoip2_db( @{$_}[ 0, 1 ], 'Test' )\n        for (\n#        [ 'GeoIP2-Anonymous-IP', {} ],\n        ['GeoIP2-City'],\n#        ['GeoIP2-Connection-Type'],\n        ['GeoIP2-Country'],\n#        ['GeoIP2-DensityIncome'],\n#        ['GeoIP2-Domain'],\n#        ['GeoIP2-Enterprise'],\n        ['GeoIP2-ISP'],\n#        ['GeoIP2-Precision-Enterprise'],\n#        ['GeoIP2-Static-IP-Score'],\n#        ['GeoIP2-User-Count'],\n        ['GeoLite2-ASN'],\n#        ['GeoLite2-City'],\n#        ['GeoLite2-Country'],\n        );\n}\n\n#sub write_broken_geoip2_city_db {\n#    no warnings 'redefine';\n#\n#    # This is how we _used_ to encode doubles. Storing them this way with the\n#    # current reader tools can lead to weird errors. This broken database is a\n#    # good way to test the robustness of reader code in the face of broken\n#    # databases.\n#    local *MaxMind::DB::Writer::Serializer::_encode_double = sub {\n#        my $self  = shift;\n#        my $value = shift;\n#\n#        $self->_simple_encode( double => $value );\n#    };\n#\n#    _write_geoip2_db( 'GeoIP2-City', 0, 'Test Broken Double Format' );\n#}\n\n#sub write_invalid_node_count {\n#    no warnings 'redefine';\n#    local *MaxMind::DB::Writer::Tree::node_count = sub { 100000 };\n#\n#    _write_geoip2_db( 'GeoIP2-City', 0, 'Test Invalid Node Count' );\n#}\n\nsub _universal_map_key_type_callback {\n    my $map = {\n\n        # languages\n        de      => 'utf8_string',\n        en      => 'utf8_string',\n        es      => 'utf8_string',\n        fr      => 'utf8_string',\n        ja      => 'utf8_string',\n        'pt-BR' => 'utf8_string',\n        ru      => 'utf8_string',\n        'zh-CN' => 'utf8_string',\n\n        # production\n        accuracy_radius                => 'uint16',\n        autonomous_system_number       => 'uint32',\n        autonomous_system_organization => 'utf8_string',\n        average_income                 => 'uint32',\n        city                           => 'map',\n        code                           => 'utf8_string',\n        confidence                     => 'uint16',\n        connection_type                => 'utf8_string',\n        continent                      => 'map',\n        country                        => 'map',\n        domain                         => 'utf8_string',\n        geoname_id                     => 'uint32',\n        ipv4_24                        => 'uint32',\n        ipv4_32                        => 'uint32',\n        ipv6_32                        => 'uint32',\n        ipv6_48                        => 'uint32',\n        ipv6_64                        => 'uint32',\n        is_anonymous                   => 'boolean',\n        is_anonymous_proxy             => 'boolean',\n        is_anonymous_vpn               => 'boolean',\n        is_hosting_provider            => 'boolean',\n        is_in_european_union           => 'boolean',\n        is_legitimate_proxy            => 'boolean',\n        is_public_proxy                => 'boolean',\n        is_residential_proxy           => 'boolean',\n        is_satellite_provider          => 'boolean',\n        is_tor_exit_node               => 'boolean',\n        iso_code                       => 'utf8_string',\n        isp                            => 'utf8_string',\n        latitude                       => 'double',\n        location                       => 'map',\n        longitude                      => 'double',\n        metro_code                     => 'uint16',\n        names                          => 'map',\n        organization                   => 'utf8_string',\n        population_density             => 'uint32',  #FIXME: Was 'uint32' but that makes the Java code crash!\n        postal                         => 'map',\n        registered_country             => 'map',\n        represented_country            => 'map',\n        score                          => 'double',\n        static_ip_score                => 'double',\n        subdivisions                   => [ 'array', 'map' ],\n        time_zone                      => 'utf8_string',\n        traits                         => 'map',\n        traits                         => 'map',\n        type                           => 'utf8_string',\n        user_type                      => 'utf8_string',\n\n        # for testing only\n        foo       => 'utf8_string',\n        bar       => 'utf8_string',\n        buzz      => 'utf8_string',\n        our_value => 'utf8_string',\n    };\n\n    my $callback = sub {\n        my $key = shift;\n\n        return $map->{$key} || die <<\"ERROR\";\nUnknown tree key '$key'.\n\nThe universal_map_key_type_callback doesn't know what type to use for the passed\nkey.  If you are adding a new key that will be used in a frozen tree / mmdb then\nyou should update the mapping in both our internal code and here.\nERROR\n    };\n\n    return $callback;\n}\n\nsub _write_geoip2_db {\n    my $type                            = shift;\n    my $populate_all_networks_with_data = shift;\n    my $description                     = shift;\n\n    my $writer = MaxMind::DB::Writer::Tree->new(\n        ip_version    => 6,\n        record_size   => 28,\n        ip_version    => 6,\n        database_type => $type,\n        languages     => [ 'en', $type eq 'GeoIP2-City' ? ('zh') : () ],\n        description   => {\n            en => ( $type =~ s/-/ /gr )\n                . \" $description Database (fake GeoIP2 data, for example purposes only)\",\n            $type eq 'GeoIP2-City' ? ( zh => '小型数据库' ) : (),\n        },\n        alias_ipv6_to_ipv4    => 1,\n        map_key_type_callback => _universal_map_key_type_callback(),\n    );\n\n    $writer->_set_build_epoch(1); # Fake timestamp to make the test files stable\n\n    _populate_all_networks( $writer, $populate_all_networks_with_data )\n        if $populate_all_networks_with_data;\n\n    my $value = shift;\n    my $nodes\n        = decode_json( read_binary(\"$Dir/../source-data/$type-Test.json\") );\n\n    for my $node (@$nodes) {\n        for my $network ( keys %$node ) {\n            $writer->insert_network(\n                Net::Works::Network->new_from_string( string => $network ),\n                $node->{$network}\n            );\n        }\n    }\n\n    my $suffix = $description =~ s/ /-/gr;\n    open my $output_fh, '>', \"$Dir/$type-$suffix.mmdb\";\n    $writer->write_tree($output_fh);\n    close $output_fh;\n\n    return;\n}\n\nsub _populate_all_networks {\n    my $writer = shift;\n    my $data   = shift;\n\n    my $max_uint128 = uint128(0) - 1;\n    my @networks    = Net::Works::Network->range_as_subnets(\n        Net::Works::Address->new_from_integer(\n            integer => 0,\n            version => 6,\n        ),\n        Net::Works::Address->new_from_integer(\n            integer => $max_uint128,\n            version => 6,\n        ),\n    );\n\n    for my $network (@networks) {\n        $writer->insert_network( $network => $data );\n    }\n}\n\n# sub write_no_ipv4_tree_db {\n#     my $subnets = shift;\n#\n#     my $writer = MaxMind::DB::Writer::Tree->new(\n#         ip_version    => 6,\n#         record_size   => 24,\n#         ip_version    => 6,\n#         database_type => 'MaxMind DB No IPv4 Search Tree',\n#         languages     => ['en'],\n#         description   => {\n#             en => 'MaxMind DB No IPv4 Search Tree',\n#         },\n#         remove_reserved_networks => 0,\n#         root_data_type           => 'utf8_string',\n#         map_key_type_callback    => sub { {} },\n#     );\n#\n#     my $subnet = Net::Works::Network->new_from_string( string => '::/64' );\n#     $writer->insert_network( $subnet, $subnet->as_string() );\n#\n#     open my $output_fh, '>', \"$Dir/MaxMind-DB-no-ipv4-search-tree.mmdb\";\n#     $writer->write_tree($output_fh);\n#     close $output_fh;\n#\n#     return;\n# }\n\n# The point of this database is to provide something where we can test looking\n# up a single value. In other words, each IP address points to a non-compound\n# value, a string rather than a map or array.\n# sub write_no_map_db {\n#     my $subnets = shift;\n#\n#     my $writer = MaxMind::DB::Writer::Tree->new(\n#         ip_version    => 4,\n#         record_size   => 24,\n#         database_type => 'MaxMind DB String Value Entries',\n#         languages     => ['en'],\n#         description   => {\n#             en =>\n#                 'MaxMind DB String Value Entries (no maps or arrays as values)',\n#         },\n#         root_data_type        => 'utf8_string',\n#         map_key_type_callback => sub { {} },\n#     );\n#\n#     for my $subnet ( @{$subnets} ) {\n#         $writer->insert_network( $subnet, $subnet->as_string() );\n#     }\n#\n#     open my $output_fh, '>', \"$Dir/MaxMind-DB-string-value-entries.mmdb\";\n#     $writer->write_tree($output_fh);\n#     close $output_fh;\n#\n#     return;\n# }\n\n# sub write_test_serialization_data {\n#     my $serializer = MaxMind::DB::Writer::Serializer->new(\n#         map_key_type_callback => sub { 'utf8_string' } );\n#\n#     $serializer->store_data( map => { long_key  => 'long_value1' } );\n#     $serializer->store_data( map => { long_key  => 'long_value2' } );\n#     $serializer->store_data( map => { long_key2 => 'long_value1' } );\n#     $serializer->store_data( map => { long_key2 => 'long_value2' } );\n#     $serializer->store_data( map => { long_key  => 'long_value1' } );\n#     $serializer->store_data( map => { long_key2 => 'long_value2' } );\n#\n#     open my $fh, '>', 'maps-with-pointers.raw';\n#     print {$fh} ${ $serializer->buffer() }\n#         or die \"Cannot write to maps-with-pointers.raw: $!\";\n#     close $fh;\n#\n#     return;\n# }\n\n# sub write_db_with_metadata_pointers {\n#     my $repeated_string = 'Lots of pointers in metadata';\n#     my $writer          = MaxMind::DB::Writer::Tree->new(\n#         ip_version            => 6,\n#         record_size           => 24,\n#         map_key_type_callback => sub { 'utf8_string' },\n#         database_type         => $repeated_string,\n#         languages             => [ 'en', 'es', 'zh' ],\n#         description           => {\n#             en => $repeated_string,\n#             es => $repeated_string,\n#             zh => $repeated_string,\n#         },\n#\n#     );\n#\n#     _populate_all_networks( $writer, {} );\n#\n#     open my $fh, '>', 'MaxMind-DB-test-metadata-pointers.mmdb';\n#\n#     $writer->write_tree($fh);\n#\n#     close $fh;\n# }\n\nmain();\n"
  },
  {
    "path": "LICENSE",
    "content": "\n                                 Apache License\n                           Version 2.0, January 2004\n                        https://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       https://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README-Hive.md",
    "content": "Hive\n====\n\nThe SerDe (it's really only a Deserializer) can be used present an Apache HTTPD logfile as a table in Hive.\n\nThis is an annotated example on how you could make the logfiles directly accessible through Hive.\n\nFirst we must ensure that Hive has the right jar file available. This can be either using the ADD JAR option in the Hive Cli\n or by installing it on the cluster.\n\n    ADD JAR target/httpdlog-serde-*-udf.jar;\n\nWe can now define an external table with column types are STRING, BIGINT and DOUBLE.\n\n    CREATE EXTERNAL TABLE nbasjes.clicks (\n         ip           STRING\n        ,timestamp    BIGINT\n        ,useragent    STRING\n        ,referrer     STRING\n        ,bui          STRING\n        ,screenHeight BIGINT\n        ,screenWidth  BIGINT\n    )\n\nOf course we must specify the class name of the Deserializer that does the heavy lifting.\n\n    ROW FORMAT SERDE 'nl.basjes.parse.apachehttpdlog.ApacheHttpdlogDeserializer'\n\nThe big part of the config lies in the SERDEPROPERTIES.\n\nThere are currently 4 types of options you can/must put in there:\n\n- \"logformat\" = \"[Apache httpd logformat]\"\n- \"field:[columnname]\" = \"[Field]\"\n- \"map:[field]\" = \"[new type]\"\n- \"load:[classname that implements Dissector]\" = \"[initialization string send to the initializeFromSettingsParameter method]\"\n\nNote that the order of various settings in the SERDEPROPERTIES is irrelevant.\n\n    WITH SERDEPROPERTIES (\n\n**\"logformat\" = \"[Apache httpd logformat]\"**\n\nThis is the Logformat specification straight from the apache httpd config file.\n\n        \"logformat\"       = \"%h %l %u %t \\\"%r\\\" %>s %b \\\"%{Referer}i\\\" \\\"%{User-Agent}i\\\" \\\"%{Cookie}i\\\" %T %V\"\n\n**\"field:[columnname]\" = \"[Field]\"**\n\nFor each column this type of property is needed for the system to know where to get the content from.\n\n        ,\"field:timestamp\" = \"TIME.EPOCH:request.receive.time.epoch\"\n        ,\"field:ip\"        = \"IP:connection.client.host\"\n        ,\"field:useragent\" = \"HTTP.USERAGENT:request.user-agent\"\n\n**\"map:[field]\" = \"[new type]\"**\n\nOnly used when mapping a specific field to a different type.\n\n        ,\"map:request.firstline.uri.query.g\"=\"HTTP.URI\"\n        ,\"map:request.firstline.uri.query.r\"=\"HTTP.URI\"\n\n        ,\"field:referrer\"  = \"STRING:request.firstline.uri.query.g.query.referrer\"\n        ,\"field:bui\"       = \"HTTP.COOKIE:request.cookies.bui\"\n\n**\"load:[classname that implements Dissector]\" = \"[initialization string send to the initializeFromSettingsParameter method]\"**\n\nOnly used when there is a custom Dissector implementation that needs to be loaded in addition to the regular Dissectors.\n\n        ,\"load:nl.basjes.parse.httpdlog.dissectors.ScreenResolutionDissector\" = \"x\"\n        ,\"map:request.firstline.uri.query.s\" = \"SCREENRESOLUTION\"\n        ,\"field:screenHeight\" = \"SCREENHEIGHT:request.firstline.uri.query.s.height\"\n        ,\"field:screenWidth\"  = \"SCREENWIDTH:request.firstline.uri.query.s.width\"\n    )\n\nFinally we define that this is stored as a TEXTFILE and where the files are located.\n\n    STORED AS TEXTFILE\n    LOCATION \"/user/nbasjes/clicks\";\n\n\nComplete example\n====\n\n    ADD JAR target/httpdlog-serde-*-udf.jar;\n\n    CREATE EXTERNAL TABLE nbasjes.clicks (\n         ip           STRING\n        ,timestamp    BIGINT\n        ,useragent    STRING\n        ,referrer     STRING\n        ,bui          STRING\n        ,screenHeight BIGINT\n        ,screenWidth  BIGINT\n    )\n\n    ROW FORMAT SERDE 'nl.basjes.parse.apachehttpdlog.ApacheHttpdlogDeserializer'\n\n    WITH SERDEPROPERTIES (\n\n        \"logformat\"       = \"%h %l %u %t \\\"%r\\\" %>s %b \\\"%{Referer}i\\\" \\\"%{User-Agent}i\\\" \\\"%{Cookie}i\\\" %T %V\"\n\n        ,\"field:timestamp\" = \"TIME.EPOCH:request.receive.time.epoch\"\n        ,\"field:ip\"        = \"IP:connection.client.host\"\n        ,\"field:useragent\" = \"HTTP.USERAGENT:request.user-agent\"\n\n        ,\"map:request.firstline.uri.query.g\"=\"HTTP.URI\"\n        ,\"map:request.firstline.uri.query.r\"=\"HTTP.URI\"\n\n        ,\"field:referrer\"  = \"STRING:request.firstline.uri.query.g.query.referrer\"\n        ,\"field:bui\"       = \"HTTP.COOKIE:request.cookies.bui\"\n\n        ,\"load:nl.basjes.parse.httpdlog.dissectors.ScreenResolutionDissector\" = \"x\"\n        ,\"map:request.firstline.uri.query.s\" = \"SCREENRESOLUTION\"\n        ,\"field:screenHeight\" = \"SCREENHEIGHT:request.firstline.uri.query.s.height\"\n        ,\"field:screenWidth\"  = \"SCREENWIDTH:request.firstline.uri.query.s.width\"\n    )\n    STORED AS TEXTFILE\n    LOCATION \"/user/nbasjes/clicks\";\n\nLicense\n===\n    Apache HTTPD & NGINX Access log parsing made easy\n    Copyright (C) 2011-2023 Niels Basjes\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n    https://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n"
  },
  {
    "path": "README-Java.md",
    "content": "Apache HTTPD logparser\n===\nThis is a Logparsing framework intended to make parsing Apache HTTPD logfiles much easier.\n\nThe basic idea is that you should be able to have a parser that you can construct by simply\ntelling it with what configuration options the line was written.\n\nUsage (Java)\n===\nFor the Java API there is an annotation based parser.\n\nFirst you put something like this in your pom.xml file:\n\n    <dependency>\n        <groupId>nl.basjes.parse.httpdlog</groupId>\n        <artifactId>httpdlog-parser</artifactId>\n        <version>6.0.0</version>\n    </dependency>\n\nI assume we have a logformat variable that looks something like this:\n\n    String logformat = \"%h %l %u %t \\\"%r\\\" %>s %b \\\"%{Referer}i\\\" \\\"%{User-Agent}i\\\"\";\n\n**Step 1: What CAN we get from this line?**\n\nTo figure out what values we CAN get from this line we instantiate the parser with a dummy class\nthat does not have ANY @Field annotations. The \"Object\" class will do just fine for this purpose.\n\n    Parser<Object> dummyParser = new HttpdLoglineParser<Object>(Object.class, logformat);\n    List<String> possiblePaths = dummyParser.getPossiblePaths();\n    for (String path: possiblePaths) {\n        System.out.println(path);\n    }\n\nYou will get a list that looks something like this:\n\n    IP:connection.client.host\n    NUMBER:connection.client.logname\n    STRING:connection.client.user\n    TIME.STAMP:request.receive.time\n    TIME.DAY:request.receive.time.day\n    TIME.MONTHNAME:request.receive.time.monthname\n    TIME.MONTH:request.receive.time.month\n    TIME.YEAR:request.receive.time.year\n    TIME.HOUR:request.receive.time.hour\n    TIME.MINUTE:request.receive.time.minute\n    TIME.SECOND:request.receive.time.second\n    TIME.MILLISECOND:request.receive.time.millisecond\n    TIME.ZONE:request.receive.time.timezone\n    HTTP.FIRSTLINE:request.firstline\n    HTTP.METHOD:request.firstline.method\n    HTTP.URI:request.firstline.uri\n    HTTP.QUERYSTRING:request.firstline.uri.query\n    STRING:request.firstline.uri.query.*\n    HTTP.PROTOCOL:request.firstline.protocol\n    HTTP.PROTOCOL.VERSION:request.firstline.protocol.version\n    STRING:request.status.last\n    BYTESCLF:response.body.bytes\n    HTTP.URI:request.referer\n    HTTP.QUERYSTRING:request.referer.query\n    STRING:request.referer.query.*\n    HTTP.USERAGENT:request.user-agent\n\nNow some of these lines contain a * .\nThis is a wildcard that can be replaced with any 'name' if you need a specific value.\nYou can also leave the '*' and get everything that is found in the actual log line.\n\n**Step 2 Create the receiving POJO**\n\nWe need to create the receiving record class that is simply a POJO that does not need any interface or inheritance.\nIn this class we create setters that will be called when the specified field has been found in the line.\n\nSo we can now add to this class a setter that simply receives a single value:\n\n    @Field(\"IP:connection.client.host\")\n    public void setIP(final String value) {\n        ip = value;\n    }\n\nIf we really want the name of the field we can also do this\n\n    @Field(\"STRING:request.firstline.uri.query.img\")\n    public void setQueryImg(final String name, final String value) {\n        results.put(name, value);\n    }\n\nThis latter form is very handy because this way we can obtain all values for a wildcard field\n\n    @Field(\"STRING:request.firstline.uri.query.*\")\n    public void setQueryStringValues(final String name, final String value) {\n        results.put(name, value);\n    }\n\nOr a combination of the above examples where you specify multiple field patterns\n\n    @Field({\"IP:connection.client.host\",\n            \"STRING:request.firstline.uri.query.*\"})\n    public void setValue(final String name, final String value) {\n        results.put(name, value);\n    }\n\nIn some cases you may not want to have empty/null values so starting with version 5.0 you can specify a setterPolicy:\n\n    @Field(value = \"STRING:request.firstline.uri.query.*\", setterPolicy = NOT_NULL)\n\nThe 3 possible values for the setterPolicy flag are:\n\n    ALWAYS    : Call the setter for all values: Normal, Empty and NULL.\n    NOT_NULL  : Call the setter for values: Normal and Empty, but not for NULL values.\n    NOT_EMPTY : Call the setter for values: Normal, but not for Empty and NULL values.\n\n*Notes about the setters*\n\n- Only if a value exists in the actual logline the setter will be called (mainly relevant if you want to get a specific query param or cookie).\n- If you specifiy the same field on several setters then each of these setters will be called.\n- There is NO guarantee about the order the setters will be called.\n\nHave a look at the 'examples/pojo' directory for a working example.\n\n**Step 3 Use the parser in your application.**\n\nYou create an instance of the parser\n\n    Parser<MyRecord> parser = new HttpdLoglineParser<MyRecord>(MyRecord.class, logformat);\n\nAnd then call the parse method repeatedly for each line.\nThere are two ways to do this:\n1) Let the parser create and a new instance of \"MyRecord\" for each parsed line (think about the GC consequences!!):\n\n       MyRecord record = parser.parse(logline);\n\n2) Reuse the same instance.\nSo you do this only once:\n\n       MyRecord record = new MyRecord();\n\nAnd then for each logline:\n\n    record.clear(); // Which is up to you to implement to 'reset' the record instance to it's initial/empty state.\n    parser.parse(record, logline);\n\nProject Lombok\n===\nIn case you like to use project Lombok to generate your getters and setters then using the annotations looks something like this:\n\n    @Getter @Setter(onMethod=@__(@Field(\"HTTP.COOKIE:request.cookies.foo\"))) private String foo = null;\n\nTo avoid weird effects please install the \"Lombok Plugin\" in IntelliJ IDEA to use this.\n\nLicense\n===\n    Apache HTTPD & NGINX Access log parsing made easy\n    Copyright (C) 2011-2023 Niels Basjes\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n    https://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n"
  },
  {
    "path": "README-Pig.md",
    "content": "Abandoned!\n===\nVersion 5.8 is the last version to support Apache Pig.\nThe last release of Apache Pig was in 2017 and right now (2023) the tool is effectively no longer used by anyone.\n\nLicense\n===\n    Apache HTTPD & NGINX Access log parsing made easy\n    Copyright (C) 2011-2023 Niels Basjes\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n    https://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n"
  },
  {
    "path": "README-geoip.md",
    "content": "Dissect IP using GeoIP2 information\n===\nThis project also contains a dissector that uses the [MaxMind](https://www.maxmind.com) GeoIP2 data to\ndissect IP addresses into things like Country, City, ASN, etc.\n\nWhere are the datafiles?\n---\nSimple: I didn't include them.\n\nThe data is owned by MaxMind and in order to use it you must either purchase a license for 'accurate' GeoIP2\ndata or download a 'slightly less accurate' free GeoLite2 version.\nAlso adding these files would make the repo very big.\n\nSee https://dev.maxmind.com/ for the both the paid GeoIP2 and the free GeoLite2 downloadable databases.\n\nI personally install and run the geoipupdate tool.\n\nhttps://dev.maxmind.com/geoip/geoipupdate/\n\nThe datafiles I usually work with:\n\n    /var/lib/GeoIP/GeoLite2-City.mmdb\n    /var/lib/GeoIP/GeoLite2-Country.mmdb\n    /var/lib/GeoIP/GeoIP2-ISP.mmdb\n    /var/lib/GeoIP/GeoLite2-ASN.mmdb\n\nYou can get some of those by installing geoipupdate tool with the config file /etc/GeoIP.conf\n\n    # The following UserId and LicenseKey are required placeholders:\n    UserId 999999\n    LicenseKey 000000000000\n    ProductIds GeoLite2-City GeoLite2-Country GeoLite2-ASN\n\nHow do I use it?\n===\n\nCurrently there are 4 dissectors available\n\nASN\n---\n* Class: nl.basjes.parse.httpdlog.dissectors.geoip.GeoIPASNDissector\n* Input: Needs the path to the GeoLite2-ASN.mmdb to function.\n* Output: ASN number and organization.\n\nISP\n---\n* Class: nl.basjes.parse.httpdlog.dissectors.geoip.GeoIPISPDissector\n* Input: Needs the path to the GeoIP2-ISP.mmdb or GeoLite2-ISP.mmdb to function.\n* Output: ASN number and organization, ISP name and organization.\n\nCountry\n---\n* Class: nl.basjes.parse.httpdlog.dissectors.geoip.GeoIPCountryDissector\n* Input: Needs the path to the GeoIP2-Country.mmdb or GeoLite2-Country.mmdb to function.\n* Output: Information about continent and country.\n\nCity\n---\n* Class: nl.basjes.parse.httpdlog.dissectors.geoip.GeoIPCityDissector\n* Input: Needs the path to the GeoIP2-City.mmdb or GeoLite2-City.mmdb to function.\n* Output: Information about continent, country, subdivision, city, postalcode and latitude/longitude.\n\n\nLicense\n===\n    Apache HTTPD & NGINX Access log parsing made easy\n    Copyright (C) 2011-2023 Niels Basjes\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n    https://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "Apache HTTPD & NGINX access log parser\n======================================\n[![Github actions Build status](https://img.shields.io/github/actions/workflow/status/nielsbasjes/logparser/build.yml?branch=main)](https://github.com/nielsbasjes/logparser/actions)\n[![Coverage Status](https://img.shields.io/codecov/c/github/nielsbasjes/logparser)](https://app.codecov.io/gh/nielsbasjes/logparser)\n[![License](https://img.shields.io/:license-apache-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0.html)\n[![Maven Central](https://img.shields.io/maven-central/v/nl.basjes.parse/parser-parent.svg)](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22nl.basjes.parse.httpdlog%22)\n[![If this project has business value for you then don't hesitate to support me with a small donation.](https://img.shields.io/badge/Sponsor%20me-via%20Github-red.svg)](https://github.com/sponsors/nielsbasjes)\n[![If this project has business value for you then don't hesitate to support me with a small donation.](https://img.shields.io/badge/Donations-via%20Paypal-red.svg)](https://www.paypal.me/nielsbasjes)\n\nThis is a Logparsing framework intended to make parsing [Apache HTTPD](https://httpd.apache.org/) and [NGINX](https://nginx.org/) access log files much easier.\n\nThe basic idea is that you should be able to have a parser that you can construct by simply\ntelling it with what configuration options the line was written.\nThese configuration options are the schema of the access loglines.\n\nSo we are using the LogFormat that wrote the file as the input parameter for the parser that reads the same file.\nIn addition to the config options specified in the Apache HTTPD manual under\n[Custom Log Formats](https://httpd.apache.org/docs/current/mod/mod_log_config.html) the following are also recognized:\n\n* common\n* combined\n* combinedio\n* referer\n* agent\n\nFor Nginx the log_format tokens are specified [here](https://nginx.org/en/docs/http/ngx_http_log_module.html#log_format) and [here](https://nginx.org/en/docs/http/ngx_http_core_module.html#variables).\n\n**Special notes about the Apache HTTPD token %{format}t**\n===\n\nQuote from [Apache HTTPD manual](https://httpd.apache.org/docs/current/mod/mod_log_config.html#formats)\n\n    %{format}t: The time, in the form given by format, which should be in strftime(3) format. (potentially localized)\n\n* **Version 2.5 and before**:\nIt cannot be extracted. A simple workaround for this limitation: replace the **%{...}t** with **%{timestamp}i** .\nYou will then get this timestamp field as if it was a request header: HTTP.HEADER:request.header.timestamp\n* **Version 2.6 and newer**: You will receive it as a textual *TIME.LOCALIZEDSTRING:request.header.time* which cannot be extracted any further.\n* **Version 3.0 and newer**: Support for parsing the customized time as long as all elements can be mapped to fields supported by joda-time.\nThis means that many fields are supported, but not all. Check the implementation in the [StrfTimeStampDissector](../v3.1/httpdlog/httpdlog-parser/src/main/java/nl/basjes/parse/httpdlog/dissectors/StrfTimeStampDissector.java#L140) class to see which are and are not supported.\n* **Version 4.0 and newer**: Switched to parsing using native java 8 time library supports a few fields differently. See [StrfTimeToDateTimeFormatter](/httpdlog/httpdlog-parser/src/main/java/nl/basjes/parse/httpdlog/dissectors/StrfTimeToDateTimeFormatter.java#L119).\n\n**Limitation**: Only a single %{format}t entry is supported per line.\nExamples as described in the LogFormat [examples section](https://httpd.apache.org/docs/current/mod/mod_log_config.html#examples)\nof the Apache HTTPD manual cannot be parsed.\n\n    You can use the %{format}t directive multiple times to build up a time format using the extended format tokens like msec_frac:\n    Timestamp including milliseconds\n             \"%{%d/%b/%Y %T}t.%{msec_frac}t %{%z}t\"\n\nIn this case where all %{format}t fields are only separated by fixed text you can rewrite this example like this\n\n    \"%{%d/%b/%Y %T}t.%{msec_frac}t %{%z}t\"\n    \"%{%d/%b/%Y %T.msec_frac %z}t\"\n\nAlthough the latter is NOT supported by Apache HTTPD this IS supported by this logparser so the above works as expected.\n\n\nAnalyze almost anything\n===\nI wrote this parser for practical reallife situations. In reality a lot happens that is not allowed when looking at the\nofficial specifications, yet in production they do happen.\nSo several of the key parts in this parser try to recover from bad data where possible and thus allow to extract as\nmuch useful information as possible even if the data is not valid.\nImportant examples of this are invalid encoding characters and chopped multibyte encoded characters that are both\nextracted as best as possible.\n\nIf you have a real logline that causes a parse error then I kindly request you to submit this line, the logformat and\nthe field that triggered the error as a bug report.\n\nPre built versions\n===\nPrebuilt versions have been deployed to maven central so using it in a project is as simple as adding a dependency.\n\nSo using it in a Java based project is as simple as adding this to your dependencies\n\n    <dependency>\n        <groupId>nl.basjes.parse.httpdlog</groupId>\n        <artifactId>httpdlog-parser</artifactId>\n        <version>6.0.0</version>\n    </dependency>\n\nNote that starting with version 6.0.0 Java 21 is needed as a runtime because of several dependencies that have updated to that.\n\nBuilding\n===\nSimply type : mvn package\nand the whole thing should build.\n\nJava, Apache {Hadoop, Hive, Drill, Flink, Beam}\n===\nI'm a big user of bigdata tools like Apache Hadoop, Hive, etc. .\nSo in here are also a Hadoop inputformat and a Hive/HCatalog Serde that are wrappers around this library.\n\nUsage (Overview)\n===\nThe framework needs two things:\n\n* The format specification in which the logfile was written (straight from the original apache httpd config file).\n* The identifiers for the fields that you want.\n\nTo obtain all the identifiers the system CAN extract from the specified logformat a separate\ndeveloper call exists in various languages that allows you to get the list of all possible values.\n\nLanguages and Tools\n===\nThe languages that are supported in this version:\n\n* [Java](README-Java.md)\n\nPrebuilt plugins for these are provided in the distribution:\n* [Apache Hive](README-Hive.md)\n\nFor tools like Apache Flink and Beam there is only example code that is also used to verify that the build\nstill works on those systems.\n* [Apache Flink](examples/apache-flink/src/test/java/nl/basjes/parse/httpdlog/flink)\n* [Apache Beam](examples/apache-beam/src/test/java/nl/basjes/parse/httpdlog/beam)\n\nTools that ship a version of this parser in their distribution\n* ~~[Apache Pig](https://github.com/apache/pig/blob/trunk/contrib/piggybank/java/src/main/java/org/apache/pig/piggybank/storage/apachelog/LogFormatLoader.java)~~\n* [Apache Drill](https://drill.apache.org/docs/httpd-format-plugin/)\n\nInternal structure and type remapping\n===\nThe basic model of this system is a tree.\nEach node in the tree has a 'type' and a 'name'.\nThe 'type' is really a 'what is the format of this string' indicator. Because there are many more of those kinds of types than your average String or Long you will see a lot of different names.\nThe 'name' is the \"breadbrumb\" towards the point in the tree where this is located.\n\nA 'Dissector' is a class that can cut a specific type (format) into a bunch of new parts that each extend the base name and have their own type.\nBecause an internal parser is constructed at the start of running a parser this tree has some dynamic properties.\nTo start only the tree is constructed for the elements actually requested. This is done to avoid 'dissecting' something that is not wanted.\nSo the parser will have a different structure depending on the requested output.\n\nThese dynamic properties also allow 'mapping a field to a different type'. Lets illustrate what this is by looking at the most common usecase.\nAssume you are trying to parse the logline for a pixel that was written by a webanalytics product. In that scenario it is common that the URL is that of a pixel and one of the query string parameters contains the actual URL. Now by default a querystring parameter gets the type STRING (which really means that is is arbitrary and cannot be dissected any further). Using this remapping (see API details per language) we can now say that a specific query string parameter really has the type HTTP.URL. As a consequence the system can now continue dissecting this specific query string parameter into things like the host, port and query string parameters.\n\nAll that is needed to map the 'g' and 'r' parameters so they are dissected further is this:\n\nJava: Call these against the parser instance right after construction\n\n    parser.addTypeRemapping(\"request.firstline.uri.query.g\", \"HTTP.URI\", Casts.STRING_ONLY);\n    parser.addTypeRemapping(\"request.firstline.uri.query.r\", \"HTTP.URI\", Casts.STRING_ONLY);\n\nHive: Add these to the SERDEPROPERTIES\n\n    \"map:request.firstline.uri.query.g\"=\"HTTP.URI\",\n    \"map:request.firstline.uri.query.r\"=\"HTTP.URI\",\n\nSpecial Dissectors\n===\n**mod_unique_id**\n\nIf you have a log field / request header that gets filled using mod_unique_id you can now peek inside\nthe values that were used to construct this.\n\n**NOTE: https://httpd.apache.org/docs/current/mod/mod_unique_id.html clearly states**\n\n     it should be emphasized that applications should not dissect the encoding.\n     Applications should treat the entire encoded UNIQUE_ID as an opaque token,\n     which can be compared against other UNIQUE_IDs for equality only.\n\nWhen you choose to ignore the clear 'should not' statement then simply add\na type remapping to map the field to the type *MOD_UNIQUE_ID*\n\n**GeoIP parsing**\n\nHead for the separate [README](README-geoip.md) file for information about this dissector.\n\nParsing problems with Jetty generated logfiles\n==============================================\nIn Jetty there is the option to create a logfile in what they call the NCSARequestLog format.\nIt was found that (historically) this had two formatting problems which cause parse errors:\n\n1. If the useragent is missing the empty value is logged with an extra ' ' after it.\n   The fix for this in Jetty was committed on 2016-07-27 in the Jetty 9.3.x and 9.4.x branches\n2. Before jetty-9.2.4.v20141103 if there is no user available the %u field is logged as \" - \"\n(i.e. with two extra spaces around the '-').\n\nTo workaround these problems you can easily start the parser with a two line logformat:\n\n    ENABLE JETTY FIX\n    %h %l %u %t \\\"%r\\\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %D\n\nThis *ENABLE JETTY FIX* is a 'magic' value that causes the underlying parser to enable the workaround for both of these problems.\nIn order for this to work correctly the useragent field must look exactly like this: *\"%{User-Agent}i\"*\n\nDonations\n===\nIf this project has business value for you then don't hesitate to support me with a small donation.\n\n[![If this project has business value for you then don't hesitate to support me with a small donation.](https://img.shields.io/badge/Sponsor%20me-via%20Github-red.svg)](https://github.com/sponsors/nielsbasjes)\n[![If this project has business value for you then don't hesitate to support me with a small donation.](https://img.shields.io/badge/Donations-via%20Paypal-red.svg)](https://www.paypal.me/nielsbasjes)\n\n\nLicense\n===\n    Apache HTTPD & NGINX Access log parsing made easy\n    Copyright (C) 2011-2023 Niels Basjes\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n    https://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n"
  },
  {
    "path": "_config.yml",
    "content": "theme: jekyll-theme-cayman"
  },
  {
    "path": "devtools/docker/Dockerfile",
    "content": "#\n# Apache HTTPD & NGINX Access log parsing made easy\n# Copyright (C) 2011-2023 Niels Basjes\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\nFROM ubuntu:26.04\n\nWORKDIR /root\n\nENV INSIDE_DOCKER Yes\n\nARG DEBIAN_FRONTEND=noninteractive\n\nWORKDIR /root\n\nSHELL [\"/bin/bash\", \"-o\", \"pipefail\", \"-c\"]\n\n#####\n# Disable suggests/recommends\n#####\nRUN echo APT::Install-Recommends \"0\"\\; > /etc/apt/apt.conf.d/10disableextras\nRUN echo APT::Install-Suggests \"0\"\\; >>  /etc/apt/apt.conf.d/10disableextras\n\nENV DEBIAN_FRONTEND noninteractive\nENV DEBCONF_TERSE true\n\n###\n# Update and install common packages\n###\nRUN apt -q update \\\n   && apt install -y software-properties-common apt-utils apt-transport-https ca-certificates \\\n   && add-apt-repository -y ppa:deadsnakes/ppa\n\nRUN apt-get -q install -y --no-install-recommends \\\n    bash-completion \\\n    build-essential \\\n    bzip2 \\\n    wget \\\n    curl \\\n    docker.io \\\n    git \\\n    gnupg-agent \\\n    rsync \\\n    sudo \\\n    vim \\\n    locales \\\n    wget \\\n    time \\\n    ruby \\\n    openjdk-8-jdk \\\n    openjdk-11-jdk \\\n    openjdk-17-jdk\n\n###\n# Set the locale ( see https://stackoverflow.com/a/28406007/114196 )\n###\nRUN sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen && \\\n    locale-gen\nENV LANG en_US.UTF-8\nENV LANGUAGE en_US:en\nENV LC_ALL en_US.UTF-8\n\n# --------------------------------\n# Install Maven\nENV MAVEN_VERSION=3.8.3\nRUN mkdir -p /usr/local/apache-maven\nRUN cd /usr/local/ && wget \"https://www.apache.org/dyn/closer.lua?action=download&filename=/maven/maven-3/${MAVEN_VERSION}/binaries/apache-maven-${MAVEN_VERSION}-bin.tar.gz\" -O \"apache-maven-${MAVEN_VERSION}-bin.tar.gz\"\nRUN cd /usr/local/ && tar xzf apache-maven-${MAVEN_VERSION}-bin.tar.gz --strip-components 1 -C /usr/local/apache-maven\nENV M2_HOME /usr/local/apache-maven\nENV PATH ${M2_HOME}/bin:${PATH}\n\n# Avoid out of memory errors in builds\nENV MAVEN_OPTS -Xms256m -Xmx512m\n\n# Install command line completion for maven\nRUN wget https://raw.githubusercontent.com/juven/maven-bash-completion/master/bash_completion.bash -O /etc/bash_completion.d/maven\n\n# --------------------------------\n# Install shellcheck\nRUN cd /usr/local/bin && \\\n     wget https://github.com/koalaman/shellcheck/releases/download/stable/shellcheck-stable.linux.x86_64.tar.xz && \\\n     tar xJf shellcheck-stable.linux.x86_64.tar.xz && \\\n     mv shellcheck-stable/shellcheck . && \\\n     rm -rf shellcheck-stable*\n\n# --------------------------------\n# Install Hugo\nENV HUGO_VERSION=0.89.0\nRUN cd /usr/local/bin && \\\n     wget https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_${HUGO_VERSION}_Linux-64bit.tar.gz && \\\n     tar xzf hugo_*.tar.gz\n\n# --------------------------------\n# Add a welcome message and environment checks.\nRUN mkdir /scripts\nADD *.sh /scripts/\nRUN chmod 755 /scripts/*.sh\n\n# --------------------------------\n# For serving the documentation site\nEXPOSE 1313\n"
  },
  {
    "path": "devtools/docker/bashcolors.sh",
    "content": "#!/bin/bash\n\n#\n# Yet Another UserAgent Analyzer\n# Copyright (C) 2013-2022 Niels Basjes\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an AS IS BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n#https://wiki.archlinux.org/index.php/Color_Bash_Prompt\n# Reset\nexport Color_Off='\\e[0m'      # Text Reset\n\n# Regular Colors\nexport Black='\\e[0;30m'       # Black\nexport Red='\\e[0;31m'         # Red\nexport Green='\\e[0;32m'       # Green\nexport Yellow='\\e[0;33m'      # Yellow\nexport Blue='\\e[0;34m'        # Blue\nexport Purple='\\e[0;35m'      # Purple\nexport Cyan='\\e[0;36m'        # Cyan\nexport White='\\e[0;37m'       # White\n\n# Bold\nexport BBlack='\\e[1;30m'      # Black\nexport BRed='\\e[1;31m'        # Red\nexport BGreen='\\e[1;32m'      # Green\nexport BYellow='\\e[1;33m'     # Yellow\nexport BBlue='\\e[1;34m'       # Blue\nexport BPurple='\\e[1;35m'     # Purple\nexport BCyan='\\e[1;36m'       # Cyan\nexport BWhite='\\e[1;37m'      # White\n\n# Underline\nexport UBlack='\\e[4;30m'      # Black\nexport URed='\\e[4;31m'        # Red\nexport UGreen='\\e[4;32m'      # Green\nexport UYellow='\\e[4;33m'     # Yellow\nexport UBlue='\\e[4;34m'       # Blue\nexport UPurple='\\e[4;35m'     # Purple\nexport UCyan='\\e[4;36m'       # Cyan\nexport UWhite='\\e[4;37m'      # White\n\n# Background\nexport On_Black='\\e[40m'      # Black\nexport On_Red='\\e[41m'        # Red\nexport On_Green='\\e[42m'      # Green\nexport On_Yellow='\\e[43m'     # Yellow\nexport On_Blue='\\e[44m'       # Blue\nexport On_Purple='\\e[45m'     # Purple\nexport On_Cyan='\\e[46m'       # Cyan\nexport On_White='\\e[47m'      # White\n\n# High Intensity\nexport IBlack='\\e[0;90m'      # Black\nexport IRed='\\e[0;91m'        # Red\nexport IGreen='\\e[0;92m'      # Green\nexport IYellow='\\e[0;93m'     # Yellow\nexport IBlue='\\e[0;94m'       # Blue\nexport IPurple='\\e[0;95m'     # Purple\nexport ICyan='\\e[0;96m'       # Cyan\nexport IWhite='\\e[0;97m'      # White\n\n# Bold High Intensity\nexport BIBlack='\\e[1;90m'     # Black\nexport BIRed='\\e[1;91m'       # Red\nexport BIGreen='\\e[1;92m'     # Green\nexport BIYellow='\\e[1;93m'    # Yellow\nexport BIBlue='\\e[1;94m'      # Blue\nexport BIPurple='\\e[1;95m'    # Purple\nexport BICyan='\\e[1;96m'      # Cyan\nexport BIWhite='\\e[1;97m'     # White\n\n# High Intensity backgrounds\nexport On_IBlack='\\e[0;100m'  # Black\nexport On_IRed='\\e[0;101m'    # Red\nexport On_IGreen='\\e[0;102m'  # Green\nexport On_IYellow='\\e[0;103m' # Yellow\nexport On_IBlue='\\e[0;104m'   # Blue\nexport On_IPurple='\\e[0;105m' # Purple\nexport On_ICyan='\\e[0;106m'   # Cyan\nexport On_IWhite='\\e[0;107m'  # White\n"
  },
  {
    "path": "devtools/docker/build_env_checks.sh",
    "content": "#!/bin/bash\n\n#\n# Apache HTTPD & NGINX Access log parsing made easy\n# Copyright (C) 2011-2023 Niels Basjes\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# -------------------------------------------------------\nfunction showWelcome {\n\n# http://patorjk.com/software/taag/#p=display&f=Slant&t=LogParser%20Builder\ncat << \"Welcome-message\"\n     __                ____                               ____        _ __    __\n    / /   ____  ____ _/ __ \\____ ______________  _____   / __ )__  __(_) /___/ /__  _____\n   / /   / __ \\/ __ `/ /_/ / __ `/ ___/ ___/ _ \\/ ___/  / __  / / / / / / __  / _ \\/ ___/\n  / /___/ /_/ / /_/ / ____/ /_/ / /  (__  )  __/ /     / /_/ / /_/ / / / /_/ /  __/ /\n /_____/\\____/\\__, /_/    \\__,_/_/  /____/\\___/_/     /_____/\\__,_/_/_/\\__,_/\\___/_/\n             /____/\n\nThis is the standard LogParser build environment.\nIn here all tools needed to run a build are present.\n\nWelcome-message\n}\n\n# -------------------------------------------------------\n\nfunction showAbort {\n# http://patorjk.com/software/taag/#p=display&f=Doom&t=Aborting...\n  cat << \"Abort-message\"\n\n    ___  _                _   _\n   / _ \\| |              | | (_)\n  / /_\\ \\ |__   ___  _ __| |_ _ _ __   __ _\n  |  _  | '_ \\ / _ \\| '__| __| | '_ \\ / _` |\n  | | | | |_) | (_) | |  | |_| | | | | (_| |_ _ _\n  \\_| |_/_.__/ \\___/|_|   \\__|_|_| |_|\\__, (_|_|_)\n                                       __/ |\n                                      |___/\n\nAbort-message\n}\n\n# -------------------------------------------------------\n\nfunction failIfUserIsRoot {\n    if [ \"$(id -u)\" -eq \"0\" ]; # If you are root then something went wrong.\n    then\n        cat <<End-of-message\n\nApparently you are inside this docker container as the user root.\nPutting it simply:\n\n   This should not occur.\n\nKnown possible causes of this are:\n1) Running this script as the root user ( Just don't )\n2) Running an old docker version ( upgrade to 1.4.1 or higher )\n\nEnd-of-message\n\n    showAbort\n\n    logout\n\n    fi\n}\n\n# -------------------------------------------------------\n\n# Configurable low water mark in GiB\nMINIMAL_MEMORY_GiB=2\n\nfunction warnIfLowMemory {\n    MINIMAL_MEMORY=$((MINIMAL_MEMORY_GiB*1024*1024)) # Convert to KiB\n    INSTALLED_MEMORY=$(grep -F MemTotal /proc/meminfo | awk '{print $2}')\n    if [ $((INSTALLED_MEMORY)) -le $((MINIMAL_MEMORY)) ];\n    then\n        cat << \"End-of-message\"\n   _                    ___  ___\n  | |                   |  \\/  |\n  | |     _____      __ | .  . | ___ _ __ ___   ___  _ __ _   _\n  | |    / _ \\ \\ /\\ / / | |\\/| |/ _ \\ '_ ` _ \\ / _ \\| '__| | | |\n  | |___| (_) \\ V  V /  | |  | |  __/ | | | | | (_) | |  | |_| |\n  \\_____/\\___/ \\_/\\_/   \\_|  |_/\\___|_| |_| |_|\\___/|_|   \\__, |\n                                                           __/ |\n                                                          |___/\nEnd-of-message\ncat << \"End-of-message\"\nYour system is running on very little memory.\nThis means it may work but it wil most likely be slower than needed.\n\nIf you are running this via boot2docker you can simply increase\nthe available memory to atleast ${MINIMAL_MEMORY_GiB} GiB (you have $((INSTALLED_MEMORY/(1024*1024))) GiB )\nEnd-of-message\n    fi\n}\n\n# -------------------------------------------------------\n\nshowWelcome\nwarnIfLowMemory\nfailIfUserIsRoot\n\n# -------------------------------------------------------\n"
  },
  {
    "path": "devtools/docker/configure-for-user.sh",
    "content": "#!/bin/bash\n\n#\n# Apache HTTPD & NGINX Access log parsing made easy\n# Copyright (C) 2011-2023 Niels Basjes\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# Native Linux (direct or via sudo)\nUSER_NAME=$1\nUSER_ID=$2\nGROUP_ID=$3\n\n\ngroupadd --non-unique -g \"${GROUP_ID}\" \"${USER_NAME}\"\nuseradd -g \"${GROUP_ID}\" -u \"${USER_ID}\" -k /root -m \"${USER_NAME}\"\n\nchown \"${USER_NAME}\" \"/home/${USER_NAME}\"\n\n{\necho \"export HOME=/home/${USER_NAME}\"\necho \"export USER=${USER_NAME}\"\necho '. /scripts/env.sh'\n} >> \"/home/${USER_NAME}/.bashrc\"\n\nVBOXSF_GROUP_LINE=$4\nif [[ -n \"${VBOXSF_GROUP_LINE}\" ]];\nthen\n    echo \"${VBOXSF_GROUP_LINE}\" >> /etc/group\n    usermod -aG vboxsf \"${USER_NAME}\"\nfi\n\necho \"${USER_NAME}    ALL=(ALL) NOPASSWD: ALL\" >> \"/etc/sudoers.d/${USER_NAME}\"\n"
  },
  {
    "path": "devtools/docker/env.sh",
    "content": "#!/bin/bash\n#\n# Apache HTTPD & NGINX Access log parsing made easy\n# Copyright (C) 2011-2023 Niels Basjes\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an AS IS BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n. \"/scripts/build_env_checks.sh\"\n. \"/scripts/bashcolors.sh\"\n\nexport JDK_VERSION=\"???\"\n\nfunction __INTERNAL__SwitchJDK {\n    JDK=$1\n    echo -e \"${IRed}${On_Black}Setting JDK to version ${JDK}${Color_Off}\"\n    sudo update-java-alternatives --set $(update-java-alternatives -l | fgrep \"${JDK}\" | sed 's@ \\+@ @g' | cut -d' ' -f1);\n    export JAVA_HOME=$(update-java-alternatives -l | fgrep \"${JDK}\" | sed 's@ \\+@ @g' | cut -d' ' -f3);\n#    export JDK_VERSION=\"JDK ${JDK}\"\n}\necho \"Use switch-jdk8, switch-jdk11 or switch-jdk17 to select the desired JDK to build with.\"\nalias switch-jdk8=\"__INTERNAL__SwitchJDK 1.8.0; export JDK_VERSION=JDK-8\"\nalias switch-jdk11=\"__INTERNAL__SwitchJDK 1.11.0; export JDK_VERSION=JDK-11\"\nalias switch-jdk17=\"__INTERNAL__SwitchJDK 1.17.0 ; export JDK_VERSION=JDK-17\"\n#alias switch-jdk13=\"__INTERNAL__SwitchJDK latest; export JDK_VERSION=JDK-13\"\n\nswitch-jdk11\n\n. \"/usr/lib/git-core/git-sh-prompt\"\nexport PS1='\\['${IBlue}${On_Black}'\\] \\u@\\['${IWhite}${On_Red}'\\][Yauaa Builder \\['${BWhite}${On_Blue}'\\]<'\\${JDK_VERSION}'>\\['${IWhite}${On_Red}'\\]]\\['${IBlue}${On_Black}'\\]:\\['${Cyan}${On_Black}'\\]\\w$(declare -F __git_ps1 &>/dev/null && __git_ps1 \" \\['${BIPurple}'\\]{\\['${BIGreen}'\\]%s\\['${BIPurple}'\\]}\")\\['${BIBlue}'\\] ]\\['${Color_Off}'\\]\\n$ '\n\nalias documentation-serve=\"cd ~/yauaa/documentation && hugo server -b http://localhost --bind=0.0.0.0\"\n"
  },
  {
    "path": "devtools/docker/prompt.sh",
    "content": "#!/bin/bash\n\n#\n# Apache HTTPD & NGINX Access log parsing made easy\n# Copyright (C) 2011-2023 Niels Basjes\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an AS IS BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n#https://wiki.archlinux.org/index.php/Color_Bash_Prompt\n# Reset\nColor_Off='\\e[0m'      # Text Reset\n\n# Regular Colors\n#Black='\\e[0;30m'       # Black\n#Red='\\e[0;31m'         # Red\n#Green='\\e[0;32m'       # Green\n#Yellow='\\e[0;33m'      # Yellow\n#Blue='\\e[0;34m'        # Blue\n#Purple='\\e[0;35m'      # Purple\nCyan='\\e[0;36m'        # Cyan\n#White='\\e[0;37m'       # White\n\n# Bold\n#BBlack='\\e[1;30m'      # Black\n#BRed='\\e[1;31m'        # Red\n#BGreen='\\e[1;32m'      # Green\n#BYellow='\\e[1;33m'     # Yellow\n#BBlue='\\e[1;34m'       # Blue\n#BPurple='\\e[1;35m'     # Purple\n#BCyan='\\e[1;36m'       # Cyan\n#BWhite='\\e[1;37m'      # White\n\n# Underline\n#UBlack='\\e[4;30m'      # Black\n#URed='\\e[4;31m'        # Red\n#UGreen='\\e[4;32m'      # Green\n#UYellow='\\e[4;33m'     # Yellow\n#UBlue='\\e[4;34m'       # Blue\n#UPurple='\\e[4;35m'     # Purple\n#UCyan='\\e[4;36m'       # Cyan\n#UWhite='\\e[4;37m'      # White\n\n# Background\nOn_Black='\\e[40m'      # Black\nOn_Red='\\e[41m'        # Red\n#On_Green='\\e[42m'      # Green\n#On_Yellow='\\e[43m'     # Yellow\n#On_Blue='\\e[44m'       # Blue\n#On_Purple='\\e[45m'     # Purple\n#On_Cyan='\\e[46m'       # Cyan\n#On_White='\\e[47m'      # White\n\n# High Intensity\n#IBlack='\\e[0;90m'      # Black\n#IRed='\\e[0;91m'        # Red\n#IGreen='\\e[0;92m'      # Green\n#IYellow='\\e[0;93m'     # Yellow\nIBlue='\\e[0;94m'       # Blue\n#IPurple='\\e[0;95m'     # Purple\n#ICyan='\\e[0;96m'       # Cyan\nIWhite='\\e[0;97m'      # White\n\n# Bold High Intensity\n#BIBlack='\\e[1;90m'     # Black\n#BIRed='\\e[1;91m'       # Red\nBIGreen='\\e[1;92m'     # Green\n#BIYellow='\\e[1;93m'    # Yellow\nBIBlue='\\e[1;94m'      # Blue\nBIPurple='\\e[1;95m'    # Purple\n#BICyan='\\e[1;96m'      # Cyan\n#BIWhite='\\e[1;97m'     # White\n\n# High Intensity backgrounds\n#On_IBlack='\\e[0;100m'  # Black\n#On_IRed='\\e[0;101m'    # Red\n#On_IGreen='\\e[0;102m'  # Green\n#On_IYellow='\\e[0;103m' # Yellow\n#On_IBlue='\\e[0;104m'   # Blue\n#On_IPurple='\\e[0;105m' # Purple\n#On_ICyan='\\e[0;106m'   # Cyan\n#On_IWhite='\\e[0;107m'  # White\n\nexport PS1='\\['${IBlue}${On_Black}'\\] \\u@\\['${IWhite}${On_Red}'\\][LogParser Builder]\\['${IBlue}${On_Black}'\\]:\\['${Cyan}${On_Black}'\\]\\w$(declare -F __git_ps1 &>/dev/null && __git_ps1 \" \\['${BIPurple}'\\]{\\['${BIGreen}'\\]%s\\['${BIPurple}'\\]}\")\\['${BIBlue}'\\] ]\\['${Color_Off}'\\]\\n$ '\n"
  },
  {
    "path": "devtools/logformat.conf",
    "content": "#\n# Apache HTTPD & NGINX Access log parsing made easy\n# Copyright (C) 2011-2023 Niels Basjes\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# For testing (almost) all log format specifiers\n\nLogFormat \"\\\"%%\\\" \\\"%a\\\" \\\"%{c}a\\\" \\\"%A\\\" \\\"%B\\\" \\\"%b\\\" \\\"%D\\\" \\\"%f\\\" \\\"%h\\\" \\\"%H\\\" \\\"%k\\\" \\\"%l\\\" \\\"%L\\\" \\\"%m\\\" \\\"%p\\\" \\\"%{canonical}p\\\" \\\"%{local}p\\\" \\\"%{remote}p\\\" \\\"%P\\\" \\\"%{pid}P\\\" \\\"%{tid}P\\\" \\\"%{hextid}P\\\" \\\"%q\\\" \\\"%r\\\" \\\"%R\\\" \\\"%s\\\" \\\"%>s\\\" \\\"%t\\\" \\\"%{msec}t\\\" \\\"%{begin:msec}t\\\" \\\"%{end:msec}t\\\" \\\"%{usec}t\\\" \\\"%{begin:usec}t\\\" \\\"%{end:usec}t\\\" \\\"%{msec_frac}t\\\" \\\"%{begin:msec_frac}t\\\" \\\"%{end:msec_frac}t\\\" \\\"%{usec_frac}t\\\" \\\"%{begin:usec_frac}t\\\" \\\"%{end:usec_frac}t\\\" \\\"%T\\\" \\\"%u\\\" \\\"%U\\\" \\\"%v\\\" \\\"%V\\\" \\\"%X\\\" \\\"%I\\\" \\\"%O\\\" \\\"%{cookie}i\\\" \\\"%{set-cookie}o\\\" \\\"%{user-agent}i\\\" \\\"%{referer}i\\\"\" logEverything\nCustomLog \"logs/fullaccess_log\" logEverything\n\n\n\"%\" \"172.17.42.1\" \"172.17.42.1\" \"172.17.0.2\" \"4880\" \"4880\" \"652\" \"/usr/share/httpd/noindex/index.html\" \"172.17.42.1\" \"HTTP/1.1\" \"0\" \"-\" \"VG9exZ0MX@uqta4OldejvQAAAAA\" \"GET\" \"80\" \"80\" \"80\" \"43417\" \"126\" \"126\" \"140597540726848\" \"140597540726848\" \"\" \"GET / HTTP/1.1\" \"httpd/unix-directory\" \"403\" \"403\" \"[21/Nov/2014:15:48:21 +0000]\" \"1416584901018\" \"1416584901018\" \"1416584901018\" \"1416584901018010\" \"1416584901018010\" \"1416584901018670\" \"018\" \"018\" \"018\" \"018010\" \"018010\" \"018670\" \"0\" \"-\" \"/\" \"172.17.0.2\" \"172.17.0.2\" \"+\" \"367\" \"5188\" \"-\" \"-\" \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.122 Safari/537.36\" \"-\"\n\"%\" \"172.17.42.1\" \"172.17.42.1\" \"172.17.0.2\" \"0\" \"-\" \"302\" \"/usr/share/httpd/noindex/css/bootstrap.min.css\" \"172.17.42.1\" \"HTTP/1.1\" \"1\" \"-\" \"-\" \"GET\" \"80\" \"80\" \"80\" \"43417\" \"126\" \"126\" \"140597540726848\" \"140597540726848\" \"\" \"GET /css/bootstrap.min.css HTTP/1.1\" \"-\" \"304\" \"304\" \"[21/Nov/2014:15:48:21 +0000]\" \"1416584901087\" \"1416584901087\" \"1416584901087\" \"1416584901087115\" \"1416584901087115\" \"1416584901087417\" \"087\" \"087\" \"087\" \"087115\" \"087115\" \"087417\" \"0\" \"-\" \"/css/bootstrap.min.css\" \"172.17.0.2\" \"172.17.0.2\" \"+\" \"448\" \"180\" \"-\" \"-\" \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.122 Safari/537.36\" \"http://172.17.0.2/\"\n\"%\" \"172.17.42.1\" \"172.17.42.1\" \"172.17.0.2\" \"0\" \"-\" \"373\" \"/usr/share/httpd/noindex/css/open-sans.css\" \"172.17.42.1\" \"HTTP/1.1\" \"0\" \"-\" \"-\" \"GET\" \"80\" \"80\" \"80\" \"43418\" \"127\" \"127\" \"140597540726848\" \"140597540726848\" \"\" \"GET /css/open-sans.css HTTP/1.1\" \"-\" \"304\" \"304\" \"[21/Nov/2014:15:48:21 +0000]\" \"1416584901087\" \"1416584901087\" \"1416584901087\" \"1416584901087430\" \"1416584901087430\" \"1416584901087803\" \"087\" \"087\" \"087\" \"087430\" \"087430\" \"087803\" \"0\" \"-\" \"/css/open-sans.css\" \"172.17.0.2\" \"172.17.0.2\" \"+\" \"444\" \"181\" \"-\" \"-\" \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.122 Safari/537.36\" \"http://172.17.0.2/\"\n\"%\" \"172.17.42.1\" \"172.17.42.1\" \"172.17.0.2\" \"0\" \"-\" \"381\" \"/usr/share/httpd/noindex/images/apache_pb.gif\" \"172.17.42.1\" \"HTTP/1.1\" \"0\" \"-\" \"-\" \"GET\" \"80\" \"80\" \"80\" \"43419\" \"128\" \"128\" \"140597540726848\" \"140597540726848\" \"\" \"GET /images/apache_pb.gif HTTP/1.1\" \"-\" \"304\" \"304\" \"[21/Nov/2014:15:48:21 +0000]\" \"1416584901087\" \"1416584901087\" \"1416584901087\" \"1416584901087445\" \"1416584901087445\" \"1416584901087826\" \"087\" \"087\" \"087\" \"087445\" \"087445\" \"087826\" \"0\" \"-\" \"/images/apache_pb.gif\" \"172.17.0.2\" \"172.17.0.2\" \"+\" \"448\" \"180\" \"-\" \"-\" \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.122 Safari/537.36\" \"http://172.17.0.2/\"\n\"%\" \"172.17.42.1\" \"172.17.42.1\" \"172.17.0.2\" \"0\" \"-\" \"269\" \"/usr/share/httpd/noindex/images/poweredby.png\" \"172.17.42.1\" \"HTTP/1.1\" \"1\" \"-\" \"-\" \"GET\" \"80\" \"80\" \"80\" \"43419\" \"128\" \"128\" \"140597540726848\" \"140597540726848\" \"\" \"GET /images/poweredby.png HTTP/1.1\" \"-\" \"304\" \"304\" \"[21/Nov/2014:15:48:21 +0000]\" \"1416584901091\" \"1416584901091\" \"1416584901091\" \"1416584901091601\" \"1416584901091601\" \"1416584901091870\" \"091\" \"091\" \"091\" \"091601\" \"091601\" \"091870\" \"0\" \"-\" \"/images/poweredby.png\" \"172.17.0.2\" \"172.17.0.2\" \"+\" \"448\" \"179\" \"-\" \"-\" \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.122 Safari/537.36\" \"http://172.17.0.2/\"\n\"%\" \"172.17.42.1\" \"172.17.42.1\" \"172.17.0.2\" \"213\" \"213\" \"448\" \"/var/www/html/ladkshjfkjasdhf\" \"172.17.42.1\" \"HTTP/1.1\" \"0\" \"-\" \"-\" \"GET\" \"80\" \"80\" \"80\" \"43482\" \"136\" \"136\" \"140597540726848\" \"140597540726848\" \"\" \"GET /ladkshjfkjasdhf HTTP/1.1\" \"-\" \"404\" \"404\" \"[21/Nov/2014:15:50:45 +0000]\" \"1416585045231\" \"1416585045231\" \"1416585045231\" \"1416585045231085\" \"1416585045231085\" \"1416585045231533\" \"231\" \"231\" \"231\" \"231085\" \"231085\" \"231533\" \"0\" \"-\" \"/ladkshjfkjasdhf\" \"172.17.0.2\" \"172.17.0.2\" \"+\" \"356\" \"429\" \"-\" \"-\" \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.122 Safari/537.36\" \"-\"\n"
  },
  {
    "path": "devtools/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n Apache HTTPD & NGINX Access log parsing made easy\n Copyright (C) 2011-2023 Niels Basjes\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n https://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n-->\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n  <modelVersion>4.0.0</modelVersion>\n  <parent>\n    <artifactId>parser-parent</artifactId>\n    <groupId>nl.basjes.parse</groupId>\n    <version>6.0.1-SNAPSHOT</version>\n  </parent>\n\n  <groupId>nl.basjes.parse.devtools</groupId>\n  <artifactId>devtools</artifactId>\n  <packaging>jar</packaging>\n  <name>Parser - Developer Tools</name>\n\n  <url>https://github.com/nielsbasjes/logparser</url>\n\n  <build>\n    <plugins>\n      <plugin>\n        <groupId>org.apache.maven.plugins</groupId>\n        <artifactId>maven-deploy-plugin</artifactId>\n        <configuration>\n          <skip>true</skip>\n        </configuration>\n      </plugin>\n\n      <!-- Disable coverage analysis for this module -->\n      <plugin>\n        <groupId>org.jacoco</groupId>\n        <artifactId>jacoco-maven-plugin</artifactId>\n        <configuration>\n          <skip>true</skip>\n        </configuration>\n      </plugin>\n    </plugins>\n  </build>\n</project>\n"
  },
  {
    "path": "devtools/release.sh",
    "content": "#!/bin/bash\n#\n# Apache HTTPD & NGINX Access log parsing made easy\n# Copyright (C) 2011-2025 Niels Basjes\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# Release procedure.\n# This uses the maven-release-plugin which has been configured to ONLY modify the local git repo.\n\n# ----------------------------------------------------------------------------------------------------\nSCRIPTDIR=\"$( cd \"$( dirname \"${BASH_SOURCE[0]}\" )\" >/dev/null && pwd )\"\n\necho \"PWD: ${SCRIPTDIR}\"\n\ncd \"${SCRIPTDIR}/..\" || ( echo \"This should not be possible\" ; exit 1 )\n\n# Working directory is now the root of the project\n\n# ----------------------------------------------------------------------------------------------------\n#https://wiki.archlinux.org/index.php/Color_Bash_Prompt\n# Reset\nexport Color_Off='\\e[0m'      # Text Reset\n\n# High Intensity\nexport IRed='\\e[0;91m'        # Red\nexport IYellow='\\e[0;93m'     # Yellow\nexport IBlue='\\e[0;94m'       # Blue\nexport IWhite='\\e[0;97m'      # White\n\n# Bold High Intensity\nexport BIRed='\\e[1;91m'       # Red\nexport BIYellow='\\e[1;93m'     # Yellow\nexport BIGreen='\\e[1;92m'     # Green\nexport BIBlue='\\e[1;94m'      # Blue\n\nfunction info() {\n  echo -e \"${Color_Off}${IWhite}[${BIBlue}INFO${IWhite}] ${Color_Off}${1}\"\n}\n\nfunction pass() {\n  echo -e \"${Color_Off}${IWhite}[${BIGreen}PASS${IWhite}] ${Color_Off}${1}\"\n}\n\nfunction warn() {\n  echo -e \"${Color_Off}${IWhite}[${BIYellow}WARN${IWhite}] ${IYellow}${1}${Color_Off}\"\n}\n\nfunction fail() {\n  echo -e \"${Color_Off}${IWhite}[${BIRed}FAIL${IWhite}] ${IRed}${1}${Color_Off}\"\n}\n\nfunction die() {\n  echo -e \"${Color_Off}\"\n  echo -e \"${IWhite}[${BIRed}FAIL${IWhite}] ${IYellow}/========================================================================\"\n  echo -e \"${IWhite}[${BIRed}FAIL${IWhite}] ${IYellow}| ${BIRed} ---------->>> PROCESS WAS ABORTED <<<---------- ${IYellow}\"\n  echo -e \"${IWhite}[${BIRed}FAIL${IWhite}] ${IYellow}| ${BIRed} $* ${IYellow}\"\n  echo -e \"${IWhite}[${BIRed}FAIL${IWhite}] ${IYellow}\\\\========================================================================\"\n  echo -e \"${Color_Off}\"\n  exit 1\n}\n\n# ----------------------------------------------------------------------------------------------------\n\n# Pre flight checks\n## Ensure all has been committed\ninfo \"Checking tree status\"\nif [[ -z $(git status -s) ]]\nthen\n  pass \"Tree is clean\"\nelse\n  git status\n  die \"Tree is dirty, must commit everything\"\nfi\n\n## Ensure we have all upstream updates (like patches from Renovate)\ninfo \"Checking up to date status\"\ngit pull\ngitPullStatus=$?\nif [ ${gitPullStatus} -ne 0 ];\nthen\n    fail \"We just received changes.\"\n    exit ${gitPullStatus}\nelse\n    pass \"Everything is up to date.\"\nfi\n\n# ----------------------------------------------------------------------------------------------------\n# Forcing a manual gpg signing action to ensure the password is known\n(\n  cd /tmp || die \"Unable to enter /tmp\"\n  echo x > ReleaseProcess-$$.txt\n  gpg --clearsign ReleaseProcess-$$.txt\n  rm ReleaseProcess-$$.txt ReleaseProcess-$$.txt.asc\n)\n\ninfo \"GPG workaround: Starting\"\nrunGpgSignerInBackGround(){\n  while : ; do date ; echo \"test\" | gpg --clearsign ; sleep 10s ; done\n}\n\nrunGpgSignerInBackGround > /dev/null 2>&1 &\nGpgSignerPID=$!\n\ninfo \"GPG workaround: Running (PID=${GpgSignerPID})\"\n\nkillSigner() {\n  info \"GPG workaround: Killing (PID=${GpgSignerPID})\"\n  kill ${GpgSignerPID}\n  info \"GPG workaround: Killed\"\n}\n\ntrap killSigner EXIT\ntrap killSigner SIGINT\n\n# ----------------------------------------------------------------------------------------------------\n## Prepare the release: Make releasable version and make tag.\ninfo \"Doing release:prepare\"\nmvn release:prepare -B\nprepareStatus=$?\nif [ ${prepareStatus} -ne 0 ];\nthen\n    fail \"Release prepare failed.\"\n    exit ${prepareStatus}\nelse\n    pass \"Release prepare Success.\"\nfi\n\n# ----------------------------------------------------------------------------------------------------\n# Check if build for this tag is reproducible\ngit checkout \"$(git describe --abbrev=0)\"\n# ----------------------------------------------------------------------------------------------------\ninfo \"Publishing for reproduction check to Local repo\"\nmvn clean install -PpackageForRelease -PskipQuality\nreproCheckPublishStatus=$?\nif [ ${reproCheckPublishStatus} -ne 0 ];\nthen\n    git switch -\n    fail \"Publishing for reproduction check failed.\"\n    exit ${reproCheckPublishStatus}\nelse\n    pass \"Publishing for reproduction check Success.\"\nfi\n\n# ----------------------------------------------------------------------------------------------------\ninfo \"Checking build reproducability ... \"\nmvn clean verify artifact:compare\nreproducibleStatus=$?\ngit switch -\nif [ ${reproducibleStatus} -ne 0 ];\nthen\n    fail \"Build is NOT reproducible.\"\n    exit ${reproducibleStatus}\nelse\n    pass \"Build is reproducible.\"\nfi\n\n# ----------------------------------------------------------------------------------------------------\n# Actually run the release: Effectively mvn deploy towards Sonatype\ninfo \"Doing release:perform\"\nmvn release:perform\nperformStatus=$?\nif [ ${performStatus} -ne 0 ];\nthen\n    fail \"Release perform failed.\"\n    exit ${performStatus}\nelse\n    pass \"Release perform Success.\"\nfi\n\n# ----------------------------------------------------------------------------------------------------\n#\n# Now check SONATYPE\n#\nRELEASEVERSION=$(git describe --abbrev=0| sed 's/^v//')\n\ninfo \"Now verify Sonatype to release version ${RELEASEVERSION}\"\ninfo \"Go to https://central.sonatype.com/publishing/deployments\"\nwarn \"Press any key abort or 'c' to continue and update the website\"\nread -n 1 k <&1\nif [[ $k = c ]] ;\nthen\n  pass \"Release worked, pushing results\"\nelse\n  die \"Aborting, nothing was pushed.\"\nfi\n\n# ----------------------------------------------------------------------------------------------------\nwarn \"Now go and manually push it all\"\n\n# ----------------------------------------------------------------------------------------------------\necho \"git push\"\necho \"git push --tags\"\n\n# ----------------------------------------------------------------------------------------------------\n"
  },
  {
    "path": "devtools/src/main/resources/checkstyle/checkstyle.xml",
    "content": "<?xml version=\"1.0\"?>\n<!--\n Apache HTTPD & NGINX Access log parsing made easy\n Copyright (C) 2011-2023 Niels Basjes\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n https://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n-->\n<!DOCTYPE module PUBLIC\n    \"-//Puppy Crawl//DTD Check Configuration 1.2//EN\"\n    \"http://www.puppycrawl.com/dtds/configuration_1_2.dtd\">\n\n<module name=\"Checker\">\n  <module name=\"SuppressionFilter\">\n    <property name=\"file\" value=\"${checkstyle.suppressions.file}\"/>\n  </module>\n\n  <!-- Checks that a package.html file exists for each package. -->\n  <!-- See http://checkstyle.sf.net/config_javadoc.html#PackageHtml -->\n  <!-- module name=\"JavadocPackage\"/ -->\n\n  <!-- Checks whether files end with a new line. -->\n  <!-- See http://checkstyle.sf.net/config_misc.html#NewlineAtEndOfFile -->\n  <module name=\"NewlineAtEndOfFile\">\n    <property name=\"lineSeparator\" value=\"lf\"/>\n  </module>\n\n  <!-- Checks that property files contain the same keys. -->\n  <!-- See http://checkstyle.sf.net/config_misc.html#Translation -->\n  <module name=\"Translation\" />\n\n  <module name=\"FileTabCharacter\" />\n\n  <module name=\"FileLength\">\n    <property name=\"fileExtensions\" value=\"!yaml\"/>\n  </module>\n\n  <module name=\"LineLength\">\n    <property name=\"max\" value=\"150\" />\n    <property name=\"fileExtensions\" value=\"!yaml\"/>\n  </module>\n\n  <module name=\"SuppressWithPlainTextCommentFilter\">\n    <property name=\"offCommentFormat\" value=\"CHECKSTYLE\\.OFF\\: ([\\w\\|]+)\"/>\n    <property name=\"onCommentFormat\" value=\"CHECKSTYLE\\.ON\\: ([\\w\\|]+)\"/>\n    <property name=\"checkFormat\" value=\"$1\"/>\n  </module>\n\n  <module name=\"TreeWalker\">\n\n    <!-- Checks for Javadoc comments. -->\n    <!-- See http://checkstyle.sf.net/config_javadoc.html -->\n    <!-- <module name=\"JavadocType\"> <property name=\"scope\" value=\"public\"/> <property name=\"allowMissingParamTags\" value=\"true\"/> </module> <module name=\"JavadocStyle\"/> -->\n\n    <!-- Checks for Naming Conventions. -->\n    <!-- See http://checkstyle.sf.net/config_naming.html -->\n    <module name=\"ConstantName\" />\n    <module name=\"LocalFinalVariableName\" />\n    <module name=\"LocalVariableName\" />\n    <module name=\"MemberName\" />\n    <module name=\"MethodName\">\n      <property name=\"format\" value=\"^[a-z][a-zA-Z0-9_]*[a-zA-Z0-9]+$\"/>\n      <!--<message key=\"name.invalidPattern\"-->\n               <!--message=\"Method name ''{0}'' must match pattern ''{1}''.\"/>-->\n    </module>\n    <module name=\"PackageName\" />\n    <module name=\"ParameterName\" />\n    <module name=\"StaticVariableName\" />\n    <module name=\"TypeName\" />\n\n    <!-- Checks for Headers -->\n    <!-- See http://checkstyle.sf.net/config_header.html -->\n    <!-- <module name=\"Header\"> -->\n    <!-- The follow property value demonstrates the ability -->\n    <!-- to have access to ANT properties. In this case it uses -->\n    <!-- the ${basedir} property to allow Checkstyle to be run -->\n    <!-- from any directory within a project. See property -->\n    <!-- expansion, -->\n    <!-- http://checkstyle.sf.net/config.html#properties -->\n    <!-- <property -->\n    <!-- name=\"headerFile\" -->\n    <!-- value=\"${basedir}/java.header\"/> -->\n    <!-- </module> -->\n\n    <!-- Following interprets the header file as regular expressions. -->\n    <!-- <module name=\"RegexpHeader\"/> -->\n\n\n    <!-- Checks for imports -->\n    <!-- See http://checkstyle.sf.net/config_import.html -->\n    <module name=\"IllegalImport\" /> <!-- defaults to sun.* packages -->\n    <module name=\"AvoidStarImport\"/>\n    <module name=\"RedundantImport\"/>\n    <module name=\"UnusedImports\"/>\n\n    <module name=\"EmptyStatement\"/>\n    <module name=\"IllegalInstantiation\"/>\n    <module name=\"SimplifyBooleanExpression\"/>\n    <module name=\"SimplifyBooleanReturn\"/>\n\n    <module name=\"InterfaceIsType\"/>\n    <module name=\"ArrayTypeStyle\"/>\n\n    <!-- Checks for Size Violations. -->\n    <!-- See http://checkstyle.sf.net/config_sizes.html -->\n    <module name=\"MethodLength\">\n      <property name=\"max\" value=\"500\" />\n      <property name=\"countEmpty\" value=\"false\" />\n    </module>\n    <module name=\"ParameterNumber\" />\n\n    <!-- Checks for whitespace -->\n    <!-- See http://checkstyle.sf.net/config_whitespace.html -->\n    <module name=\"EmptyForIteratorPad\" />\n    <module name=\"MethodParamPad\" />\n    <module name=\"NoWhitespaceAfter\" />\n    <module name=\"NoWhitespaceBefore\" />\n    <module name=\"ParenPad\" />\n    <module name=\"TypecastParenPad\" />\n    <module name=\"WhitespaceAfter\">\n      <property name=\"tokens\" value=\"COMMA, SEMI\" />\n    </module>\n    <!-- Alert about trailing whitespace -->\n    <module name=\"Regexp\">\n      <property name=\"format\" value=\"[ \\t]+$\"/>\n      <property name=\"illegalPattern\" value=\"true\"/>\n      <property name=\"message\" value=\"Trailing whitespace\"/>\n    </module>\n\n    <!-- Modifier Checks -->\n    <!-- See http://checkstyle.sf.net/config_modifiers.html -->\n    <module name=\"ModifierOrder\" />\n    <module name=\"RedundantModifier\" />\n\n\n    <!-- Checks for blocks. You know, those {}'s -->\n    <!-- See http://checkstyle.sf.net/config_blocks.html -->\n    <module name=\"AvoidNestedBlocks\" />\n    <module name=\"EmptyBlock\" />\n    <module name=\"LeftCurly\" />\n    <module name=\"NeedBraces\" />\n    <module name=\"RightCurly\">\n      <property name=\"option\" value=\"same\" />\n    </module>\n\n\n    <!-- Checks for common coding problems -->\n    <!-- See http://checkstyle.sf.net/config_coding.html -->\n    <!--<module name=\"AvoidInlineConditionals\" />-->\n    <!-- <module name=\"DoubleCheckedLocking\"/> -->\n    <module name=\"EmptyStatement\" />\n    <module name=\"EqualsHashCode\" />\n    <module name=\"HiddenField\">\n      <property name=\"ignoreConstructorParameter\" value=\"true\" />\n    </module>\n    <module name=\"IllegalInstantiation\" />\n    <module name=\"InnerAssignment\" />\n    <module name=\"MissingSwitchDefault\" />\n    <module name=\"SimplifyBooleanExpression\" />\n    <module name=\"SimplifyBooleanReturn\" />\n\n    <!-- Checks for class design -->\n    <!-- See http://checkstyle.sf.net/config_design.html -->\n    <module name=\"FinalClass\" />\n    <module name=\"HideUtilityClassConstructor\" />\n    <module name=\"InterfaceIsType\" />\n    <module name=\"VisibilityModifier\">\n      <property name=\"packageAllowed\" value=\"true\"/>\n      <property name=\"protectedAllowed\" value=\"true\"/>\n    </module>\n\n    <!-- Miscellaneous other checks. -->\n    <!-- See http://checkstyle.sf.net/config_misc.html -->\n    <module name=\"ArrayTypeStyle\" />\n    <module name=\"Indentation\">\n      <property name=\"basicOffset\" value=\"4\" />\n      <property name=\"braceAdjustment\" value=\"0\" />\n      <property name=\"caseIndent\" value=\"4\" />\n      <property name=\"throwsIndent\" value=\"4\" />\n      <property name=\"arrayInitIndent\" value=\"0\" />\n      <property name=\"lineWrappingIndentation\" value=\"4\" />\n    </module>\n\n    <!-- module name=\"TodoComment\"/ -->\n    <module name=\"UpperEll\" />\n\n  </module>\n\n</module>\n"
  },
  {
    "path": "devtools/src/main/resources/checkstyle/suppressions.xml",
    "content": "<?xml version=\"1.0\"?>\n<!--\n Apache HTTPD & NGINX Access log parsing made easy\n Copyright (C) 2011-2023 Niels Basjes\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n https://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n-->\n<!DOCTYPE suppressions PUBLIC\n  \"-//Puppy Crawl//DTD Suppressions 1.1//EN\"\n  \"http://www.puppycrawl.com/dtds/suppressions_1_1.dtd\">\n\n<suppressions>\n  <!-- Avoid any reports about generated code -->\n  <suppress files=\".*[\\\\/]target[\\\\/]\" checks=\".*\" />\n\n  <!-- Junit 5 does not want 'public' -->\n  <!-- Without public this rule enforces removing public from constructors -->\n  <!-- Causing the tests to fail because now the reflection no longer works. -->\n  <suppress checks=\"RedundantModifier\" files=\".*[\\\\/]src[\\\\/](test|it)[\\\\/]\"/>\n</suppressions>\n"
  },
  {
    "path": "docs/CNAME",
    "content": "logparser.basjes.nl"
  },
  {
    "path": "docs/README.md",
    "content": "Apache HTTPD & NGINX access log parser\n======================================\n[![Github actions Build status](https://img.shields.io/github/actions/workflow/status/nielsbasjes/logparser/build.yml?branch=main)](https://github.com/nielsbasjes/logparser/actions)\n[![Coverage Status](https://img.shields.io/codecov/c/github/nielsbasjes/logparser)](https://app.codecov.io/gh/nielsbasjes/logparser)\n[![License](https://img.shields.io/:license-apache-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0.html)\n[![Maven Central](https://img.shields.io/maven-central/v/nl.basjes.parse/parser-parent.svg)](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22nl.basjes.parse.httpdlog%22)\n[![If this project has business value for you then don't hesitate to support me with a small donation.](https://img.shields.io/badge/Donations-via%20Paypal-blue.svg)](https://www.paypal.me/nielsbasjes)\n\nThis is a Logparsing framework intended to make parsing [Apache HTTPD](https://httpd.apache.org/) and [NGINX](https://nginx.org/) access log files much easier.\n\nThe basic idea is that you should be able to have a parser that you can construct by simply\ntelling it with what configuration options the line was written.\nThese configuration options are the schema of the access loglines.\n\nSo we are using the LogFormat that wrote the file as the input parameter for the parser that reads the same file.\nIn addition to the config options specified in the Apache HTTPD manual under\n[Custom Log Formats](https://httpd.apache.org/docs/current/mod/mod_log_config.html) the following are also recognized:\n\n* common\n* combined\n* combinedio\n* referer\n* agent\n\nFor Nginx the log_format tokens are specified [here](https://nginx.org/en/docs/http/ngx_http_log_module.html#log_format) and [here](https://nginx.org/en/docs/http/ngx_http_core_module.html#variables).\n\n\n*** PLACE HOLDER PAGE ***\n\n\nDonations\n===\nIf this project has business value for you then don't hesitate to support me with a small donation.\n\n[![Donations via PayPal](https://img.shields.io/badge/Donations-via%20Paypal-blue.svg)](https://www.paypal.me/nielsbasjes)\n\nLicense\n===\n    Apache HTTPD & NGINX Access log parsing made easy\n    Copyright (C) 2011-2023 Niels Basjes\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n    https://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n"
  },
  {
    "path": "examples/apache-beam/.gitignore",
    "content": "tmp\n"
  },
  {
    "path": "examples/apache-beam/pom.xml",
    "content": "<?xml version=\"1.0\"?>\n<!--\n Apache HTTPD & NGINX Access log parsing made easy\n Copyright (C) 2011-2023 Niels Basjes\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n https://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n-->\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n  <modelVersion>4.0.0</modelVersion>\n\n  <parent>\n    <artifactId>httpdlog-examples</artifactId>\n    <groupId>nl.basjes.parse.httpdlog.examples</groupId>\n    <version>6.0.1-SNAPSHOT</version>\n  </parent>\n\n  <artifactId>apache-beam</artifactId>\n  <name>Parser - Examples - Apache Beam</name>\n\n  <properties>\n    <!-- The mess of the dependencies is too big to make this check pass -->\n    <depencency-convergence.phase>none</depencency-convergence.phase>\n  </properties>\n\n\n  <dependencies>\n    <dependency>\n      <groupId>nl.basjes.parse.httpdlog</groupId>\n      <artifactId>httpdlog-parser</artifactId>\n      <version>${project.version}</version>\n    </dependency>\n\n    <dependency>\n      <groupId>org.apache.beam</groupId>\n      <artifactId>beam-sdks-java-core</artifactId>\n      <version>${beam.version}</version>\n    </dependency>\n\n    <dependency>\n      <groupId>org.projectlombok</groupId>\n      <artifactId>lombok</artifactId>\n      <version>${lombok.version}</version>\n      <scope>provided</scope>\n    </dependency>\n\n    <dependency>\n      <groupId>org.apache.avro</groupId>\n      <artifactId>avro</artifactId>\n      <version>${avro.version}</version>\n      <scope>provided</scope>\n    </dependency>\n\n    <dependency>\n      <groupId>org.apache.beam</groupId>\n      <artifactId>beam-sdks-java-core</artifactId>\n      <version>${beam.version}</version>\n      <classifier>tests</classifier>\n      <scope>test</scope>\n    </dependency>\n\n    <dependency>\n      <groupId>org.apache.beam</groupId>\n      <artifactId>beam-runners-direct-java</artifactId>\n      <version>${beam.version}</version>\n      <scope>test</scope>\n    </dependency>\n\n    <!--=========================-->\n    <!--Needed for PAssert-->\n    <dependency>\n      <groupId>org.hamcrest</groupId>\n      <artifactId>hamcrest-all</artifactId>\n      <version>1.3</version>\n      <scope>test</scope>\n    </dependency>\n\n    <!--Needed for PAssert-->\n    <!--=========================-->\n\n  </dependencies>\n\n  <build>\n    <plugins>\n\n      <plugin>\n        <groupId>org.jacoco</groupId>\n        <artifactId>jacoco-maven-plugin</artifactId>\n      </plugin>\n\n      <plugin>\n        <groupId>org.apache.maven.plugins</groupId>\n        <artifactId>maven-deploy-plugin</artifactId>\n        <configuration>\n          <skip>true</skip>\n        </configuration>\n      </plugin>\n\n      <plugin>\n        <groupId>org.apache.avro</groupId>\n        <artifactId>avro-maven-plugin</artifactId>\n        <executions>\n          <!--<execution>-->\n            <!--<id>schemas</id>-->\n            <!--<phase>generate-sources</phase>-->\n            <!--<goals>-->\n              <!--<goal>schema</goal>-->\n              <!--<goal>protocol</goal>-->\n              <!--<goal>idl-protocol</goal>-->\n            <!--</goals>-->\n            <!--<configuration>-->\n              <!--<stringType>String</stringType>-->\n              <!--<sourceDirectory>src/main/avro</sourceDirectory>-->\n              <!--<fieldVisibility>private</fieldVisibility>-->\n            <!--</configuration>-->\n          <!--</execution>-->\n          <execution>\n            <id>test-schemas</id>\n            <phase>generate-test-sources</phase>\n            <goals>\n              <goal>schema</goal>\n              <goal>protocol</goal>\n              <goal>idl-protocol</goal>\n            </goals>\n            <configuration>\n              <stringType>String</stringType>\n              <sourceDirectory>src/test/avro</sourceDirectory>\n              <fieldVisibility>private</fieldVisibility>\n            </configuration>\n          </execution>\n        </executions>\n      </plugin>\n\n    </plugins>\n  </build>\n</project>\n"
  },
  {
    "path": "examples/apache-beam/src/test/avro/Record.avdl",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n@namespace(\"nl.basjes.parse.webevents\")\n\nprotocol BeamTestRecord {\n\n  record Device {\n    long   screenWidth    ; // \"SCREENWIDTH:request.firstline.uri.query.s.width\"\n    long   screenHeight   ; // \"SCREENHEIGHT:request.firstline.uri.query.s.height\"\n  }\n\n  record Browser {\n    string useragent ; // \"STRING:request.user-agent\"\n  }\n\n  record ISP {\n    string asnNumber ;          // ASN:connection.client.host.asn.number\n    string asnOrganization ;    // STRING:connection.client.host.asn.organization\n    string ispName ;            // STRING:connection.client.host.isp.name\n    string ispOrganization ;    // STRING:connection.client.host.isp.organization\n  }\n\n  record GeoLocation {\n    string continentName;       // STRING:connection.client.host.continent.name\n    string continentCode;       // STRING:connection.client.host.continent.code\n    string countryName;         // STRING:connection.client.host.country.name\n    string countryIso;          // STRING:connection.client.host.country.iso\n    string subdivisionName;     // STRING:connection.client.host.subdivision.name\n    string subdivisionIso;      // STRING:connection.client.host.subdivision.iso\n    string cityName;            // STRING:connection.client.host.city.name\n    string postalCode;          // STRING:connection.client.host.postal.code\n    double locationLatitude;    // STRING:connection.client.host.location.latitude\n    double locationLongitude;   // STRING:connection.client.host.location.longitude\n  }\n\n  record Visitor {\n    string ip; // \"IP:connection.client.host\"\n    ISP isp;\n    GeoLocation geoLocation;\n  }\n\n  record Click {\n    long timestamp; // \"TIME.EPOCH:request.receive.time.epoch\"\n    Device  device;\n    Browser browser;\n    Visitor visitor;\n  }\n}\n"
  },
  {
    "path": "examples/apache-beam/src/test/java/nl/basjes/parse/httpdlog/beam/TestCase.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage nl.basjes.parse.httpdlog.beam;\n\nimport nl.basjes.parse.core.Parser;\nimport nl.basjes.parse.httpdlog.HttpdLoglineParser;\nimport nl.basjes.parse.httpdlog.beam.pojo.MyRecord;\nimport nl.basjes.parse.httpdlog.dissectors.geoip.GeoIPCityDissector;\nimport nl.basjes.parse.httpdlog.dissectors.geoip.GeoIPISPDissector;\n\n// CHECKSTYLE.OFF: LineLength\n// CHECKSTYLE.OFF: LeftCurly\n// CHECKSTYLE.OFF: HideUtilityClassConstructor\npublic final class TestCase {\n\n    private static final String TEST_MMDB_BASE_DIR = \"../../GeoIP2-TestData/test-data/\";\n    public static final String ISP_TEST_MMDB = TEST_MMDB_BASE_DIR + \"GeoIP2-ISP-Test.mmdb\";\n    public static final String CITY_TEST_MMDB = TEST_MMDB_BASE_DIR + \"GeoIP2-City-Test.mmdb\";\n\n    public static String getLogFormat() {\n        return \"%h %l %u %t \\\"%r\\\" %>s %b \\\"%{Referer}i\\\" \\\"%{User-Agent}i\\\" \\\"%{Cookie}i\\\"\";\n    }\n\n    public static String getInputLine() {\n        return \"2001:980:91c0:1:8d31:a232:25e5:85d - - [05/Sep/2010:11:27:50 +0200] \\\"GET /b/ss/advbolprod2/1/H.22.1/s73176445413647?AQB=1&pccr=true&vidn=27F07A1B85012045-4000011500517C43&&ndh=1&t=19%2F5%2F2012%2023%3A51%3A27%202%20-120&ce=UTF-8&ns=bol&pageName=%2Fnl%2Fp%2Ffissler-speciaal-pannen-grillpan-28-x-28-cm%2F9200000002876066%2F&g=http%3A%2F%2Fwww.bol.com%2Fnl%2Fp%2Ffissler-speciaal-pannen-grillpan-28-x-28-cm%2F9200000002876066%2F%3Fpromo%3Dkoken-pannen_303_hs-koken-pannen-afj-120601_B3_product_1_9200000002876066%26bltg.pg_nm%3Dkoken-pannen%26bltg.slt_id%3D303%26bltg.slt_nm%3Dhs-koken-pannen-afj-120601%26bltg.slt_p&r=http%3A%2F%2Fwww.bol.com%2Fnl%2Fm%2Fkoken-tafelen%2Fkoken-pannen%2FN%2F11766%2Findex.html%3Fblabla%3Dblablawashere&cc=EUR&ch=D%3Dv3&server=ps316&events=prodView%2Cevent1%2Cevent2%2Cevent31&products=%3B9200000002876066%3B%3B%3B%3Bevar3%3Dkth%7Cevar8%3D9200000002876066_Fissler%20Speciaal%20Pannen%20-%20Grillpan%20-%2028%20x%2028%20cm%7Cevar35%3D170%7Cevar47%3DKTH%7Cevar9%3DNew%7Cevar40%3Dno%20reviews%2C%3B%3B%3B%3Bevent31%3D423&c1=catalog%3Akth%3Aproduct-detail&v1=D%3Dc1&h1=catalog%2Fkth%2Fproduct-detail&h2=D%3DpageName&v3=kth&l3=endeca_001-mensen_default%2Cendeca_exact-boeken_default%2Cendeca_verschijningsjaar_default%2Cendeca_hardgoodscategoriesyn_default%2Cendeca_searchrank-hadoop_default%2Cendeca_genre_default%2Cendeca_uitvoering_default&v4=ps316&v6=koken-pannen_303_hs-koken-pannen-afj-120601_B3_product_1_9200000002876066&v10=Tu%2023%3A30&v12=logged%20in&v13=New&c25=niet%20ssl&c26=3631&c30=84.106.227.113.1323208998208762&v31=2000285551&c45=20120619235127&c46=20120501%204.3.4.1&c47=D%3Ds_vi&c49=%2Fnl%2Fcatalog%2Fproduct-detail.jsp&c50=%2Fnl%2Fcatalog%2Fproduct-detail.jsp&v51=www.bol.com&s=1280x800&c=24&j=1.7&v=N&k=Y&bw=1280&bh=272&p=Shockwave%20Flash%3B&AQE=1 HTTP/1.1\\\" 200 23617 \\\"http://www.google.nl/imgres?imgurl=http://daniel_en_sander.basjes.nl/fotos/geboorte-kaartje/geboortekaartje-binnenkant.jpg&imgrefurl=http://daniel_en_sander.basjes.nl/fotos/geboorte-kaartje&usg=__LDxRMkacRs6yLluLcIrwoFsXY6o=&h=521&w=1024&sz=41&hl=nl&start=13&zoom=1&um=1&itbs=1&tbnid=Sqml3uGbjoyBYM:&tbnh=76&tbnw=150&prev=/images%3Fq%3Dbinnenkant%2Bgeboortekaartje%26um%3D1%26hl%3Dnl%26sa%3DN%26biw%3D1882%26bih%3D1014%26tbs%3Disch:1\\\" \\\"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_4; nl-nl) AppleWebKit/533.17.8 (KHTML, like Gecko) Version/5.0.1 Safari/533.17.8\\\" \\\"jquery-ui-theme=Eggplant; BuI=SomeThing; Apache=127.0.0.1.1351111543699529\\\"\";\n    }\n\n    public static Parser<MyRecord> createTestParser() throws NoSuchMethodException {\n        Parser<MyRecord> parser = new HttpdLoglineParser<>(MyRecord.class, getLogFormat());\n\n        parser.addDissector(new nl.basjes.parse.httpdlog.dissectors.ScreenResolutionDissector());\n\n        parser.addTypeRemapping(\"request.firstline.uri.query.g\", \"HTTP.URI\");\n        parser.addTypeRemapping(\"request.firstline.uri.query.r\", \"HTTP.URI\");\n        parser.addTypeRemapping(\"request.firstline.uri.query.s\", \"SCREENRESOLUTION\");\n\n        parser.addParseTarget(\"setConnectionClientHost\", \"IP:connection.client.host\");\n        parser.addParseTarget(\"setRequestReceiveTime\",   \"TIME.STAMP:request.receive.time\");\n        parser.addParseTarget(\"setReferrer\",             \"STRING:request.firstline.uri.query.g.query.promo\");\n        parser.addParseTarget(\"setScreenResolution\",     \"STRING:request.firstline.uri.query.s\");\n        parser.addParseTarget(\"setScreenWidth\",          \"SCREENWIDTH:request.firstline.uri.query.s.width\");\n        parser.addParseTarget(\"setScreenHeight\",         \"SCREENHEIGHT:request.firstline.uri.query.s.height\");\n        parser.addParseTarget(\"setGoogleQuery\",          \"STRING:request.firstline.uri.query.r.query.blabla\");\n        parser.addParseTarget(\"setBui\",                  \"HTTP.COOKIE:request.cookies.bui\");\n        parser.addParseTarget(\"setUseragent\",            \"HTTP.USERAGENT:request.user-agent\");\n\n        parser.addDissector(new GeoIPISPDissector(ISP_TEST_MMDB));\n        parser.addParseTarget(\"setAsnNumber\",            \"ASN:connection.client.host.asn.number\");\n        parser.addParseTarget(\"setAsnOrganization\",      \"STRING:connection.client.host.asn.organization\");\n        parser.addParseTarget(\"setIspName\",              \"STRING:connection.client.host.isp.name\");\n        parser.addParseTarget(\"setIspOrganization\",      \"STRING:connection.client.host.isp.organization\");\n\n        parser.addDissector(new GeoIPCityDissector(CITY_TEST_MMDB));\n        parser.addParseTarget(\"setContinentName\",        \"STRING:connection.client.host.continent.name\");\n        parser.addParseTarget(\"setContinentCode\",        \"STRING:connection.client.host.continent.code\");\n        parser.addParseTarget(\"setCountryName\",          \"STRING:connection.client.host.country.name\");\n        parser.addParseTarget(\"setCountryIso\",           \"STRING:connection.client.host.country.iso\");\n        parser.addParseTarget(\"setSubdivisionName\",      \"STRING:connection.client.host.subdivision.name\");\n        parser.addParseTarget(\"setSubdivisionIso\",       \"STRING:connection.client.host.subdivision.iso\");\n        parser.addParseTarget(\"setCityName\",             \"STRING:connection.client.host.city.name\");\n        parser.addParseTarget(\"setPostalCode\",           \"STRING:connection.client.host.postal.code\");\n        parser.addParseTarget(\"setLocationLatitude\",     \"STRING:connection.client.host.location.latitude\");\n        parser.addParseTarget(\"setLocationLongitude\",    \"STRING:connection.client.host.location.longitude\");\n\n        return parser;\n    }\n\n    public static String getExpectedConnectionClientHost()      { return \"2001:980:91c0:1:8d31:a232:25e5:85d\"; }\n    public static String getExpectedRequestReceiveTime()        { return \"05/Sep/2010:11:27:50 +0200\"; }\n    public static Long   getExpectedRequestReceiveTimeEpoch()   { return 1283678870000L; }\n    public static String getExpectedReferrer()                  { return \"koken-pannen_303_hs-koken-pannen-afj-120601_B3_product_1_9200000002876066\"; }\n    public static String getExpectedScreenResolution()          { return \"1280x800\"; }\n    public static Long   getExpectedScreenWidth()               { return 1280L; }\n    public static Long   getExpectedScreenHeight()              { return 800L; }\n    public static String getExpectedGoogleQuery()               { return \"blablawashere\"; }\n    public static String getExpectedBui()                       { return \"SomeThing\"; }\n    public static String getExpectedUseragent()                 { return \"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_4; nl-nl) AppleWebKit/533.17.8 (KHTML, like Gecko) Version/5.0.1 Safari/533.17.8\"; }\n\n    public static String getExpectedAsnNumber()                 { return \"6666\"; }\n    public static String getExpectedAsnOrganization()           { return \"Basjes Global Network IPv6\"; }\n    public static String getExpectedIspName()                   { return \"Basjes ISP IPv6\"; }\n    public static String getExpectedIspOrganization()           { return \"Niels Basjes IPv6\"; }\n\n    public static String getExpectedContinentName()             { return \"Europe\"; }\n    public static String getExpectedContinentCode()             { return \"EU\"; }\n    public static String getExpectedCountryName()               { return \"Netherlands\"; }\n    public static String getExpectedCountryIso()                { return \"NL\"; }\n    public static String getExpectedSubdivisionName()           { return \"Noord Holland\"; }\n    public static String getExpectedSubdivisionIso()            { return \"NH\"; }\n    public static String getExpectedCityName()                  { return \"Amstelveen\"; }\n    public static String getExpectedPostalCode()                { return \"1187\"; }\n    public static Double getExpectedLocationLatitude()          { return 52.5; }\n    public static Double getExpectedLocationLongitude()         { return 5.75; }\n\n}\n"
  },
  {
    "path": "examples/apache-beam/src/test/java/nl/basjes/parse/httpdlog/beam/avro/ExpectedClick.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.httpdlog.beam.avro;\n\nimport nl.basjes.parse.webevents.Click;\n\nimport static nl.basjes.parse.httpdlog.beam.TestCase.getExpectedAsnNumber;\nimport static nl.basjes.parse.httpdlog.beam.TestCase.getExpectedAsnOrganization;\nimport static nl.basjes.parse.httpdlog.beam.TestCase.getExpectedCityName;\nimport static nl.basjes.parse.httpdlog.beam.TestCase.getExpectedConnectionClientHost;\nimport static nl.basjes.parse.httpdlog.beam.TestCase.getExpectedContinentCode;\nimport static nl.basjes.parse.httpdlog.beam.TestCase.getExpectedContinentName;\nimport static nl.basjes.parse.httpdlog.beam.TestCase.getExpectedCountryIso;\nimport static nl.basjes.parse.httpdlog.beam.TestCase.getExpectedCountryName;\nimport static nl.basjes.parse.httpdlog.beam.TestCase.getExpectedIspName;\nimport static nl.basjes.parse.httpdlog.beam.TestCase.getExpectedIspOrganization;\nimport static nl.basjes.parse.httpdlog.beam.TestCase.getExpectedLocationLatitude;\nimport static nl.basjes.parse.httpdlog.beam.TestCase.getExpectedLocationLongitude;\nimport static nl.basjes.parse.httpdlog.beam.TestCase.getExpectedPostalCode;\nimport static nl.basjes.parse.httpdlog.beam.TestCase.getExpectedRequestReceiveTimeEpoch;\nimport static nl.basjes.parse.httpdlog.beam.TestCase.getExpectedScreenHeight;\nimport static nl.basjes.parse.httpdlog.beam.TestCase.getExpectedScreenWidth;\nimport static nl.basjes.parse.httpdlog.beam.TestCase.getExpectedSubdivisionIso;\nimport static nl.basjes.parse.httpdlog.beam.TestCase.getExpectedSubdivisionName;\nimport static nl.basjes.parse.httpdlog.beam.TestCase.getExpectedUseragent;\n\n// CHECKSTYLE.OFF: HideUtilityClassConstructor\npublic class ExpectedClick {\n\n    public static Click create(){\n        Click.Builder builder = Click.newBuilder();\n\n        builder\n            .setTimestamp(getExpectedRequestReceiveTimeEpoch())\n            .getDeviceBuilder()\n                .setScreenWidth(getExpectedScreenWidth())\n                .setScreenHeight(getExpectedScreenHeight());\n\n        builder\n            .getBrowserBuilder()\n                .setUseragent(getExpectedUseragent());\n\n        builder\n            .getVisitorBuilder()\n                .setIp(getExpectedConnectionClientHost());\n\n        builder\n            .getVisitorBuilder()\n                .getIspBuilder()\n                    .setAsnNumber(getExpectedAsnNumber())\n                    .setAsnOrganization(getExpectedAsnOrganization())\n                    .setIspName(getExpectedIspName())\n                    .setIspOrganization(getExpectedIspOrganization());\n\n        builder\n            .getVisitorBuilder()\n                .getGeoLocationBuilder()\n                    .setContinentName(getExpectedContinentName())\n                    .setContinentCode(getExpectedContinentCode())\n                    .setCountryName(getExpectedCountryName())\n                    .setCountryIso(getExpectedCountryIso())\n                    .setSubdivisionName(getExpectedSubdivisionName())\n                    .setSubdivisionIso(getExpectedSubdivisionIso())\n                    .setCityName(getExpectedCityName())\n                    .setPostalCode(getExpectedPostalCode())\n                    .setLocationLatitude(getExpectedLocationLatitude())\n                    .setLocationLongitude(getExpectedLocationLongitude());\n\n        return builder.build();\n    }\n\n}\n"
  },
  {
    "path": "examples/apache-beam/src/test/java/nl/basjes/parse/httpdlog/beam/avro/TestParserDoFnAvro.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage nl.basjes.parse.httpdlog.beam.avro;\n\nimport nl.basjes.parse.core.Field;\nimport nl.basjes.parse.core.Parser;\nimport nl.basjes.parse.core.exceptions.DissectionFailure;\nimport nl.basjes.parse.core.exceptions.InvalidDissectorException;\nimport nl.basjes.parse.core.exceptions.MissingDissectorsException;\nimport nl.basjes.parse.httpdlog.HttpdLoglineParser;\nimport nl.basjes.parse.httpdlog.beam.TestCase;\nimport nl.basjes.parse.httpdlog.dissectors.ScreenResolutionDissector;\nimport nl.basjes.parse.httpdlog.dissectors.geoip.GeoIPCityDissector;\nimport nl.basjes.parse.httpdlog.dissectors.geoip.GeoIPISPDissector;\nimport nl.basjes.parse.webevents.Click;\nimport org.apache.beam.sdk.coders.StringUtf8Coder;\nimport org.apache.beam.sdk.testing.PAssert;\nimport org.apache.beam.sdk.testing.TestPipeline;\nimport org.apache.beam.sdk.transforms.Create;\nimport org.apache.beam.sdk.transforms.DoFn;\nimport org.apache.beam.sdk.transforms.ParDo;\nimport org.apache.beam.sdk.values.PCollection;\nimport org.apache.commons.lang3.builder.Builder;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.junit.runners.JUnit4;\n\nimport java.io.Serializable;\nimport java.util.Collections;\nimport java.util.List;\n\nimport static nl.basjes.parse.httpdlog.beam.TestCase.CITY_TEST_MMDB;\nimport static nl.basjes.parse.httpdlog.beam.TestCase.ISP_TEST_MMDB;\n\n// CHECKSTYLE.OFF: LineLength\n// CHECKSTYLE.OFF: LeftCurly\n@RunWith(JUnit4.class)\npublic class TestParserDoFnAvro implements Serializable {\n\n    public static class ClickSetter implements Builder<Click> {\n\n        final Click.Builder builder = Click.newBuilder();\n\n        @Field(\"TIME.EPOCH:request.receive.time.epoch\")             public void setRequestReceiveTime(Long value)       { builder.setTimestamp(value);                          }\n\n        @Field(\"SCREENWIDTH:request.firstline.uri.query.s.width\")   public void setScreenWidth(Long value)              { builder.getDeviceBuilder().setScreenWidth(value);     }\n        @Field(\"SCREENHEIGHT:request.firstline.uri.query.s.height\") public void setScreenHeight(Long value)             { builder.getDeviceBuilder().setScreenHeight(value);    }\n\n        @Field(\"HTTP.USERAGENT:request.user-agent\")                 public void setUseragent(String value)              { builder.getBrowserBuilder().setUseragent(value);      }\n\n        @Field(\"IP:connection.client.host\")                         public void setConnectionClientHost(String value)   { builder.getVisitorBuilder().setIp(value);             }\n\n        @Field(\"ASN:connection.client.host.asn.number\")             public void setAsnNumber(String value)              { builder.getVisitorBuilder().getIspBuilder().setAsnNumber(value);  }\n        @Field(\"STRING:connection.client.host.asn.organization\")    public void setAsnOrganization(String value)        { builder.getVisitorBuilder().getIspBuilder().setAsnOrganization(value);  }\n        @Field(\"STRING:connection.client.host.isp.name\")            public void setIspName(String value)                { builder.getVisitorBuilder().getIspBuilder().setIspName(value);  }\n        @Field(\"STRING:connection.client.host.isp.organization\")    public void setIspOrganization(String value)        { builder.getVisitorBuilder().getIspBuilder().setIspOrganization(value);  }\n\n        @Field(\"STRING:connection.client.host.continent.name\")      public void setContinentName(String value)          { builder.getVisitorBuilder().getGeoLocationBuilder().setContinentName(value);  }\n        @Field(\"STRING:connection.client.host.continent.code\")      public void setContinentCode(String value)          { builder.getVisitorBuilder().getGeoLocationBuilder().setContinentCode(value);  }\n        @Field(\"STRING:connection.client.host.country.name\")        public void setCountryName(String value)            { builder.getVisitorBuilder().getGeoLocationBuilder().setCountryName(value);  }\n        @Field(\"STRING:connection.client.host.country.iso\")         public void setCountryIso(String value)             { builder.getVisitorBuilder().getGeoLocationBuilder().setCountryIso(value);  }\n        @Field(\"STRING:connection.client.host.subdivision.name\")    public void setSubdivisionName(String value)        { builder.getVisitorBuilder().getGeoLocationBuilder().setSubdivisionName(value);  }\n        @Field(\"STRING:connection.client.host.subdivision.iso\")     public void setSubdivisionIso(String value)         { builder.getVisitorBuilder().getGeoLocationBuilder().setSubdivisionIso(value);  }\n        @Field(\"STRING:connection.client.host.city.name\")           public void setCityName(String value)               { builder.getVisitorBuilder().getGeoLocationBuilder().setCityName(value);  }\n        @Field(\"STRING:connection.client.host.postal.code\")         public void setPostalCode(String value)             { builder.getVisitorBuilder().getGeoLocationBuilder().setPostalCode(value);  }\n        @Field(\"STRING:connection.client.host.location.latitude\")   public void setLocationLatitude(Double value)       { builder.getVisitorBuilder().getGeoLocationBuilder().setLocationLatitude(value);  }\n        @Field(\"STRING:connection.client.host.location.longitude\")  public void setLocationLongitude(Double value)      { builder.getVisitorBuilder().getGeoLocationBuilder().setLocationLongitude(value);  }\n\n        @Override\n        public Click build() {\n            return builder.build();\n        }\n    }\n\n    public static class MyParserDoFn extends DoFn<String, Click> {\n        private Parser<ClickSetter> parser;\n\n        @Setup\n        public void setup() {\n            parser = new HttpdLoglineParser<>(ClickSetter.class, TestCase.getLogFormat())\n                .addDissector(new ScreenResolutionDissector())\n                .addTypeRemapping(\"request.firstline.uri.query.g\", \"HTTP.URI\")\n                .addTypeRemapping(\"request.firstline.uri.query.r\", \"HTTP.URI\")\n                .addTypeRemapping(\"request.firstline.uri.query.s\", \"SCREENRESOLUTION\")\n                .addDissector(new GeoIPISPDissector(ISP_TEST_MMDB))\n                .addDissector(new GeoIPCityDissector(CITY_TEST_MMDB));\n        }\n\n        @ProcessElement\n        public void processElement(ProcessContext c) throws InvalidDissectorException, MissingDissectorsException, DissectionFailure {\n            String input = c.element();\n            ClickSetter setter = parser.parse(new ClickSetter(), input);\n            if (setter == null) {\n                System.err.println(\"Something went terribly wrong\");\n                return;\n            }\n            Click click = setter.build();\n            c.output(click);\n        }\n    }\n\n\n    @Rule\n    public final transient TestPipeline pipeline = TestPipeline.create();\n\n    @Test\n    public void testClassDefinitionAvro() {\n        List<String> logLines = Collections.singletonList(TestCase.getInputLine());\n\n        // Apply Create, passing the list and the coder, to create the PCollection.\n        PCollection<String> input = pipeline.apply(Create.of(logLines)).setCoder(StringUtf8Coder.of());\n\n        PCollection<Click> filledTestRecords = input\n            .apply(\"Extract Elements from logline\",\n                ParDo.of(new MyParserDoFn()));\n\n        PAssert.that(filledTestRecords).containsInAnyOrder(ExpectedClick.create());\n\n        pipeline.run().waitUntilFinish();\n    }\n\n}\n"
  },
  {
    "path": "examples/apache-beam/src/test/java/nl/basjes/parse/httpdlog/beam/avro/TestParserDoFnAvroInline.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage nl.basjes.parse.httpdlog.beam.avro;\n\nimport nl.basjes.parse.core.Field;\nimport nl.basjes.parse.core.Parser;\nimport nl.basjes.parse.core.exceptions.DissectionFailure;\nimport nl.basjes.parse.core.exceptions.InvalidDissectorException;\nimport nl.basjes.parse.core.exceptions.MissingDissectorsException;\nimport nl.basjes.parse.httpdlog.HttpdLoglineParser;\nimport nl.basjes.parse.httpdlog.beam.TestCase;\nimport nl.basjes.parse.httpdlog.dissectors.ScreenResolutionDissector;\nimport nl.basjes.parse.httpdlog.dissectors.geoip.GeoIPCityDissector;\nimport nl.basjes.parse.httpdlog.dissectors.geoip.GeoIPISPDissector;\nimport nl.basjes.parse.webevents.Click;\nimport org.apache.beam.sdk.coders.StringUtf8Coder;\nimport org.apache.beam.sdk.testing.PAssert;\nimport org.apache.beam.sdk.testing.TestPipeline;\nimport org.apache.beam.sdk.transforms.Create;\nimport org.apache.beam.sdk.transforms.DoFn;\nimport org.apache.beam.sdk.transforms.ParDo;\nimport org.apache.beam.sdk.values.PCollection;\nimport org.apache.commons.lang3.builder.Builder;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.junit.runners.JUnit4;\n\nimport java.io.Serializable;\nimport java.util.Collections;\nimport java.util.List;\n\nimport static nl.basjes.parse.httpdlog.beam.TestCase.CITY_TEST_MMDB;\nimport static nl.basjes.parse.httpdlog.beam.TestCase.ISP_TEST_MMDB;\n\n// CHECKSTYLE.OFF: LineLength\n// CHECKSTYLE.OFF: LeftCurly\n@RunWith(JUnit4.class)\npublic class TestParserDoFnAvroInline implements Serializable {\n\n    public static class ClickSetter implements Builder<Click> {\n\n        final Click.Builder builder = Click.newBuilder();\n\n        @Field(\"TIME.EPOCH:request.receive.time.epoch\")             public void setRequestReceiveTime(Long value)       { builder.setTimestamp(value);                          }\n\n        @Field(\"SCREENWIDTH:request.firstline.uri.query.s.width\")   public void setScreenWidth(Long value)              { builder.getDeviceBuilder().setScreenWidth(value);     }\n        @Field(\"SCREENHEIGHT:request.firstline.uri.query.s.height\") public void setScreenHeight(Long value)             { builder.getDeviceBuilder().setScreenHeight(value);    }\n\n        @Field(\"HTTP.USERAGENT:request.user-agent\")                 public void setUseragent(String value)              { builder.getBrowserBuilder().setUseragent(value);      }\n\n        @Field(\"IP:connection.client.host\")                         public void setConnectionClientHost(String value)   { builder.getVisitorBuilder().setIp(value);             }\n\n        @Field(\"ASN:connection.client.host.asn.number\")             public void setAsnNumber(String value)              { builder.getVisitorBuilder().getIspBuilder().setAsnNumber(value);  }\n        @Field(\"STRING:connection.client.host.asn.organization\")    public void setAsnOrganization(String value)        { builder.getVisitorBuilder().getIspBuilder().setAsnOrganization(value);  }\n        @Field(\"STRING:connection.client.host.isp.name\")            public void setIspName(String value)                { builder.getVisitorBuilder().getIspBuilder().setIspName(value);  }\n        @Field(\"STRING:connection.client.host.isp.organization\")    public void setIspOrganization(String value)        { builder.getVisitorBuilder().getIspBuilder().setIspOrganization(value);  }\n\n        @Field(\"STRING:connection.client.host.continent.name\")      public void setContinentName(String value)          { builder.getVisitorBuilder().getGeoLocationBuilder().setContinentName(value);  }\n        @Field(\"STRING:connection.client.host.continent.code\")      public void setContinentCode(String value)          { builder.getVisitorBuilder().getGeoLocationBuilder().setContinentCode(value);  }\n        @Field(\"STRING:connection.client.host.country.name\")        public void setCountryName(String value)            { builder.getVisitorBuilder().getGeoLocationBuilder().setCountryName(value);  }\n        @Field(\"STRING:connection.client.host.country.iso\")         public void setCountryIso(String value)             { builder.getVisitorBuilder().getGeoLocationBuilder().setCountryIso(value);  }\n        @Field(\"STRING:connection.client.host.subdivision.name\")    public void setSubdivisionName(String value)        { builder.getVisitorBuilder().getGeoLocationBuilder().setSubdivisionName(value);  }\n        @Field(\"STRING:connection.client.host.subdivision.iso\")     public void setSubdivisionIso(String value)         { builder.getVisitorBuilder().getGeoLocationBuilder().setSubdivisionIso(value);  }\n        @Field(\"STRING:connection.client.host.city.name\")           public void setCityName(String value)               { builder.getVisitorBuilder().getGeoLocationBuilder().setCityName(value);  }\n        @Field(\"STRING:connection.client.host.postal.code\")         public void setPostalCode(String value)             { builder.getVisitorBuilder().getGeoLocationBuilder().setPostalCode(value);  }\n        @Field(\"STRING:connection.client.host.location.latitude\")   public void setLocationLatitude(Double value)       { builder.getVisitorBuilder().getGeoLocationBuilder().setLocationLatitude(value);  }\n        @Field(\"STRING:connection.client.host.location.longitude\")  public void setLocationLongitude(Double value)      { builder.getVisitorBuilder().getGeoLocationBuilder().setLocationLongitude(value);  }\n\n        @Override\n        public Click build() {\n            return builder.build();\n        }\n    }\n\n    @Rule\n    public final transient TestPipeline pipeline = TestPipeline.create();\n\n    @Test\n    public void testClassDefinitionAvro() {\n        List<String> logLines = Collections.singletonList(TestCase.getInputLine());\n\n        // Apply Create, passing the list and the coder, to create the PCollection.\n        PCollection<String> input = pipeline.apply(Create.of(logLines)).setCoder(StringUtf8Coder.of());\n\n        PCollection<Click> filledTestRecords = input\n            .apply(\"Extract Elements from logline\",\n                ParDo.of(new DoFn<String, Click>() {\n                    private Parser<ClickSetter> parser;\n\n                    @Setup\n                    public void setup() {\n                        parser = new HttpdLoglineParser<>(ClickSetter.class, TestCase.getLogFormat())\n                                .addDissector(new ScreenResolutionDissector())\n                                .addTypeRemapping(\"request.firstline.uri.query.g\", \"HTTP.URI\")\n                                .addTypeRemapping(\"request.firstline.uri.query.r\", \"HTTP.URI\")\n                                .addTypeRemapping(\"request.firstline.uri.query.s\", \"SCREENRESOLUTION\")\n                                .addDissector(new GeoIPISPDissector(ISP_TEST_MMDB))\n                                .addDissector(new GeoIPCityDissector(CITY_TEST_MMDB));\n                    }\n\n                    @ProcessElement\n                    public void processElement(ProcessContext c) throws InvalidDissectorException, MissingDissectorsException, DissectionFailure {\n                        String input = c.element();\n                        ClickSetter setter = parser.parse(new ClickSetter(), input);\n                        Click click = setter.build();\n                        c.output(click);\n                    }\n                }));\n\n        PAssert.that(filledTestRecords).containsInAnyOrder(ExpectedClick.create());\n\n        pipeline.run().waitUntilFinish();\n    }\n\n}\n"
  },
  {
    "path": "examples/apache-beam/src/test/java/nl/basjes/parse/httpdlog/beam/pojo/MyRecord.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage nl.basjes.parse.httpdlog.beam.pojo;\n\nimport lombok.EqualsAndHashCode;\nimport lombok.Getter;\nimport lombok.Setter;\nimport lombok.ToString;\nimport org.junit.Test;\n\nimport java.io.Serializable;\n\nimport static nl.basjes.parse.httpdlog.beam.TestCase.getExpectedAsnNumber;\nimport static nl.basjes.parse.httpdlog.beam.TestCase.getExpectedAsnOrganization;\nimport static nl.basjes.parse.httpdlog.beam.TestCase.getExpectedBui;\nimport static nl.basjes.parse.httpdlog.beam.TestCase.getExpectedCityName;\nimport static nl.basjes.parse.httpdlog.beam.TestCase.getExpectedConnectionClientHost;\nimport static nl.basjes.parse.httpdlog.beam.TestCase.getExpectedContinentCode;\nimport static nl.basjes.parse.httpdlog.beam.TestCase.getExpectedContinentName;\nimport static nl.basjes.parse.httpdlog.beam.TestCase.getExpectedCountryIso;\nimport static nl.basjes.parse.httpdlog.beam.TestCase.getExpectedCountryName;\nimport static nl.basjes.parse.httpdlog.beam.TestCase.getExpectedGoogleQuery;\nimport static nl.basjes.parse.httpdlog.beam.TestCase.getExpectedIspName;\nimport static nl.basjes.parse.httpdlog.beam.TestCase.getExpectedIspOrganization;\nimport static nl.basjes.parse.httpdlog.beam.TestCase.getExpectedLocationLatitude;\nimport static nl.basjes.parse.httpdlog.beam.TestCase.getExpectedLocationLongitude;\nimport static nl.basjes.parse.httpdlog.beam.TestCase.getExpectedPostalCode;\nimport static nl.basjes.parse.httpdlog.beam.TestCase.getExpectedReferrer;\nimport static nl.basjes.parse.httpdlog.beam.TestCase.getExpectedRequestReceiveTime;\nimport static nl.basjes.parse.httpdlog.beam.TestCase.getExpectedScreenHeight;\nimport static nl.basjes.parse.httpdlog.beam.TestCase.getExpectedScreenResolution;\nimport static nl.basjes.parse.httpdlog.beam.TestCase.getExpectedScreenWidth;\nimport static nl.basjes.parse.httpdlog.beam.TestCase.getExpectedSubdivisionIso;\nimport static nl.basjes.parse.httpdlog.beam.TestCase.getExpectedSubdivisionName;\nimport static nl.basjes.parse.httpdlog.beam.TestCase.getExpectedUseragent;\nimport static org.junit.Assert.assertEquals;\n\n// CHECKSTYLE.OFF: ParamPad\n@ToString\n@EqualsAndHashCode\npublic class MyRecord implements Serializable {\n\n    @Getter @Setter private String connectionClientHost = null;\n    @Getter @Setter private String requestReceiveTime   = null;\n    @Getter @Setter private String referrer             = null;\n    @Getter @Setter private String screenResolution     = null;\n    @Getter @Setter private Long   screenWidth          = null;\n    @Getter @Setter private Long   screenHeight         = null;\n    @Getter @Setter private String googleQuery          = null;\n    @Getter @Setter private String bui                  = null;\n    @Getter @Setter private String useragent            = null;\n\n    @Getter @Setter private String asnNumber            = null;\n    @Getter @Setter private String asnOrganization      = null;\n    @Getter @Setter private String ispName              = null;\n    @Getter @Setter private String ispOrganization      = null;\n\n    @Getter @Setter private String continentName        = null;\n    @Getter @Setter private String continentCode        = null;\n    @Getter @Setter private String countryName          = null;\n    @Getter @Setter private String countryIso           = null;\n    @Getter @Setter private String subdivisionName      = null;\n    @Getter @Setter private String subdivisionIso       = null;\n    @Getter @Setter private String cityName             = null;\n    @Getter @Setter private String postalCode           = null;\n    @Getter @Setter private Double locationLatitude     = null;\n    @Getter @Setter private Double locationLongitude    = null;\n\n    public void assertIsValid() {\n        assertEquals(getExpectedConnectionClientHost(), getConnectionClientHost());\n        assertEquals(getExpectedRequestReceiveTime(),   getRequestReceiveTime());\n        assertEquals(getExpectedReferrer(),             getReferrer());\n        assertEquals(getExpectedScreenResolution(),     getScreenResolution());\n        assertEquals(getExpectedScreenWidth(),          getScreenWidth());\n        assertEquals(getExpectedScreenHeight(),         getScreenHeight());\n        assertEquals(getExpectedGoogleQuery(),          getGoogleQuery());\n        assertEquals(getExpectedBui(),                  getBui());\n        assertEquals(getExpectedUseragent(),            getUseragent());\n\n        assertEquals(getExpectedAsnNumber(),            getAsnNumber());\n        assertEquals(getExpectedAsnOrganization(),      getAsnOrganization());\n        assertEquals(getExpectedIspName(),              getIspName());\n        assertEquals(getExpectedIspOrganization(),      getIspOrganization());\n\n        assertEquals(getExpectedContinentName(),        getContinentName());\n        assertEquals(getExpectedContinentCode(),        getContinentCode());\n        assertEquals(getExpectedSubdivisionName(),      getSubdivisionName());\n        assertEquals(getExpectedSubdivisionIso(),       getSubdivisionIso());\n        assertEquals(getExpectedCountryName(),          getCountryName());\n        assertEquals(getExpectedCountryIso(),           getCountryIso());\n        assertEquals(getExpectedCityName(),             getCityName());\n        assertEquals(getExpectedPostalCode(),           getPostalCode());\n        assertEquals(getExpectedLocationLatitude(),     getLocationLatitude());\n        assertEquals(getExpectedLocationLongitude(),    getLocationLongitude());\n    }\n\n    public MyRecord setFullValid() {\n        setConnectionClientHost (getExpectedConnectionClientHost());\n        setRequestReceiveTime   (getExpectedRequestReceiveTime());\n        setReferrer             (getExpectedReferrer());\n        setScreenResolution     (getExpectedScreenResolution());\n        setScreenWidth          (getExpectedScreenWidth());\n        setScreenHeight         (getExpectedScreenHeight());\n        setGoogleQuery          (getExpectedGoogleQuery());\n        setBui                  (getExpectedBui());\n        setUseragent            (getExpectedUseragent());\n\n        setAsnNumber            (getExpectedAsnNumber());\n        setAsnOrganization      (getExpectedAsnOrganization());\n        setIspName              (getExpectedIspName());\n        setIspOrganization      (getExpectedIspOrganization());\n\n        setContinentName        (getExpectedContinentName());\n        setContinentCode        (getExpectedContinentCode());\n        setCountryName          (getExpectedCountryName());\n        setCountryIso           (getExpectedCountryIso());\n        setSubdivisionName      (getExpectedSubdivisionName());\n        setSubdivisionIso       (getExpectedSubdivisionIso());\n        setCityName             (getExpectedCityName());\n        setPostalCode           (getExpectedPostalCode());\n        setLocationLatitude     (getExpectedLocationLatitude());\n        setLocationLongitude    (getExpectedLocationLongitude());\n\n        return this;\n    }\n\n\n    @Test\n    public void checkTestMethodsPass() {\n        MyRecord testRecord = new MyRecord().setFullValid();\n        testRecord.assertIsValid();\n    }\n\n    @Test(expected = AssertionError.class)\n    public void checkTestMethodsFail() {\n        MyRecord testRecord = new MyRecord();\n        testRecord.assertIsValid();\n    }\n\n}\n"
  },
  {
    "path": "examples/apache-beam/src/test/java/nl/basjes/parse/httpdlog/beam/pojo/TestParserDoFnClass.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage nl.basjes.parse.httpdlog.beam.pojo;\n\nimport nl.basjes.parse.core.Parser;\nimport nl.basjes.parse.core.exceptions.DissectionFailure;\nimport nl.basjes.parse.core.exceptions.InvalidDissectorException;\nimport nl.basjes.parse.core.exceptions.MissingDissectorsException;\nimport nl.basjes.parse.httpdlog.beam.TestCase;\nimport org.apache.beam.sdk.coders.StringUtf8Coder;\nimport org.apache.beam.sdk.testing.PAssert;\nimport org.apache.beam.sdk.testing.TestPipeline;\nimport org.apache.beam.sdk.transforms.Create;\nimport org.apache.beam.sdk.transforms.DoFn;\nimport org.apache.beam.sdk.transforms.ParDo;\nimport org.apache.beam.sdk.values.PCollection;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.junit.runners.JUnit4;\n\nimport java.io.Serializable;\nimport java.util.Collections;\nimport java.util.List;\n\n\n@RunWith(JUnit4.class)\npublic class TestParserDoFnClass implements Serializable {\n\n    public static class MyParserDoFn extends DoFn<String, MyRecord> {\n        private final Parser<MyRecord> parser;\n\n        public MyParserDoFn() throws NoSuchMethodException {\n            super();\n            parser = TestCase.createTestParser();\n        }\n\n        @ProcessElement\n        public void processElement(ProcessContext c) throws InvalidDissectorException, MissingDissectorsException, DissectionFailure {\n            c.output(parser.parse(c.element()));\n        }\n    }\n\n    @Rule\n    public final transient TestPipeline pipeline = TestPipeline.create();\n\n    @Test\n    public void testClassDefinition() throws Exception {\n        List<String> logLines = Collections.singletonList(TestCase.getInputLine());\n\n        // Apply Create, passing the list and the coder, to create the PCollection.\n        PCollection<String> input = pipeline.apply(Create.of(logLines)).setCoder(StringUtf8Coder.of());\n\n        PCollection<MyRecord> filledTestRecords = input\n            .apply(\"Extract Elements from logline\",\n                ParDo.of(new MyParserDoFn()));\n\n        MyRecord expected = new MyRecord().setFullValid();\n\n        PAssert.that(filledTestRecords).containsInAnyOrder(expected);\n\n        pipeline.run().waitUntilFinish();\n    }\n\n}\n"
  },
  {
    "path": "examples/apache-beam/src/test/java/nl/basjes/parse/httpdlog/beam/pojo/TestParserDoFnInline.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage nl.basjes.parse.httpdlog.beam.pojo;\n\nimport nl.basjes.parse.core.Parser;\nimport nl.basjes.parse.core.exceptions.DissectionFailure;\nimport nl.basjes.parse.core.exceptions.InvalidDissectorException;\nimport nl.basjes.parse.core.exceptions.MissingDissectorsException;\nimport nl.basjes.parse.httpdlog.beam.TestCase;\nimport org.apache.beam.sdk.coders.StringUtf8Coder;\nimport org.apache.beam.sdk.testing.PAssert;\nimport org.apache.beam.sdk.testing.TestPipeline;\nimport org.apache.beam.sdk.transforms.Create;\nimport org.apache.beam.sdk.transforms.DoFn;\nimport org.apache.beam.sdk.transforms.ParDo;\nimport org.apache.beam.sdk.values.PCollection;\nimport org.junit.Rule;\nimport org.junit.Test;\n\nimport java.io.Serializable;\nimport java.util.Collections;\nimport java.util.List;\n\n\npublic class TestParserDoFnInline implements Serializable {\n\n    @Rule\n    public final transient TestPipeline pipeline = TestPipeline.create();\n\n    @Test\n    public void testInlineDefinition() {\n        List<String> logLines = Collections.singletonList(TestCase.getInputLine());\n\n        // Apply Create, passing the list and the coder, to create the PCollection.\n        PCollection<String> input = pipeline.apply(Create.of(logLines)).setCoder(StringUtf8Coder.of());\n\n        PCollection<MyRecord> filledTestRecords = input\n            .apply(\"Extract Elements from logline\",\n                ParDo.of(new DoFn<String, MyRecord>() {\n                    private Parser<MyRecord> parser;\n\n                    @Setup\n                    public void setup() throws NoSuchMethodException {\n                        parser = TestCase.createTestParser();\n                    }\n\n                    @ProcessElement\n                    public void processElement(ProcessContext c) throws InvalidDissectorException, MissingDissectorsException, DissectionFailure {\n                        c.output(parser.parse(c.element()));\n                    }\n                }));\n\n        MyRecord expected = new MyRecord().setFullValid();\n\n        PAssert.that(filledTestRecords).containsInAnyOrder(expected);\n\n        pipeline.run().waitUntilFinish();\n    }\n}\n"
  },
  {
    "path": "examples/apache-beam/src/test/resources/log4j.properties",
    "content": "#\n# Apache HTTPD & NGINX Access log parsing made easy\n# Copyright (C) 2011-2023 Niels Basjes\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# Root logger option\nlog4j.rootLogger=DEBUG, stdout\n#, file\nlog4j.appender.stdout=org.apache.log4j.ConsoleAppender\nlog4j.appender.stdout.Target=System.out\nlog4j.appender.stdout.threshold=INFO\nlog4j.appender.stdout.layout=org.apache.log4j.PatternLayout\nlog4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} [%-5p] %-40c{1}:%5L: %m%n\n## file appender\n#log4j.appender.file=org.apache.log4j.RollingFileAppender\n#log4j.appender.file.File=target/debug.log\n#log4j.appender.file.threshold=DEBUG\n#log4j.appender.file.layout=org.apache.log4j.PatternLayout\n#log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd} %d{ABSOLUTE} [%-5p] %-40c{1}:%5L: %m%n\n#log4j.appender.file.Append=false\n"
  },
  {
    "path": "examples/apache-flink/.gitignore",
    "content": "tmp\n"
  },
  {
    "path": "examples/apache-flink/pom.xml",
    "content": "<?xml version=\"1.0\"?>\n<!--\n Apache HTTPD & NGINX Access log parsing made easy\n Copyright (C) 2011-2023 Niels Basjes\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n https://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n-->\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n  <modelVersion>4.0.0</modelVersion>\n\n  <parent>\n    <artifactId>httpdlog-examples</artifactId>\n    <groupId>nl.basjes.parse.httpdlog.examples</groupId>\n    <version>6.0.1-SNAPSHOT</version>\n  </parent>\n\n  <artifactId>apache-flink</artifactId>\n  <name>Parser - Examples - Apache Flink</name>\n\n  <dependencies>\n    <dependency>\n      <groupId>nl.basjes.parse.httpdlog</groupId>\n      <artifactId>httpdlog-parser</artifactId>\n      <version>${project.version}</version>\n    </dependency>\n\n    <dependency>\n      <groupId>org.projectlombok</groupId>\n      <artifactId>lombok</artifactId>\n      <version>${lombok.version}</version>\n      <scope>provided</scope>\n    </dependency>\n\n    <dependency>\n      <groupId>org.apache.avro</groupId>\n      <artifactId>avro</artifactId>\n      <version>${avro.version}</version>\n      <scope>provided</scope>\n    </dependency>\n\n    <dependency>\n      <groupId>org.apache.flink</groupId>\n      <artifactId>flink-avro</artifactId>\n      <version>${flink.version}</version>\n      <scope>provided</scope>\n      <exclusions>\n        <exclusion>\n          <groupId>org.apache.avro</groupId>\n          <artifactId>avro</artifactId>\n        </exclusion>\n        <exclusion>\n          <groupId>org.slf4j</groupId>\n          <artifactId>slf4j-api</artifactId>\n        </exclusion>\n      </exclusions>\n    </dependency>\n\n    <dependency>\n      <groupId>org.apache.flink</groupId>\n      <artifactId>flink-core</artifactId>\n      <version>${flink.version}</version>\n      <scope>provided</scope>\n      <exclusions>\n        <exclusion>\n          <groupId>org.slf4j</groupId>\n          <artifactId>slf4j-api</artifactId>\n        </exclusion>\n        <exclusion>\n          <groupId>org.apache.commons</groupId>\n          <artifactId>commons-lang3</artifactId>\n        </exclusion>\n        <exclusion>\n          <groupId>commons-collections</groupId>\n          <artifactId>commons-collections</artifactId>\n        </exclusion>\n      </exclusions>\n    </dependency>\n\n    <dependency>\n      <groupId>org.apache.flink</groupId>\n      <artifactId>flink-clients</artifactId>\n      <version>${flink.version}</version>\n      <scope>test</scope>\n      <exclusions>\n        <exclusion>\n          <groupId>org.slf4j</groupId>\n          <artifactId>slf4j-api</artifactId>\n        </exclusion>\n        <exclusion>\n          <groupId>org.apache.commons</groupId>\n          <artifactId>commons-lang3</artifactId>\n        </exclusion>\n      </exclusions>\n    </dependency>\n\n  </dependencies>\n\n  <build>\n    <plugins>\n\n      <plugin>\n        <groupId>org.jacoco</groupId>\n        <artifactId>jacoco-maven-plugin</artifactId>\n      </plugin>\n\n      <plugin>\n        <groupId>org.apache.maven.plugins</groupId>\n        <artifactId>maven-deploy-plugin</artifactId>\n        <configuration>\n          <skip>true</skip>\n        </configuration>\n      </plugin>\n\n      <plugin>\n        <groupId>org.apache.avro</groupId>\n        <artifactId>avro-maven-plugin</artifactId>\n        <executions>\n          <!--<execution>-->\n            <!--<id>schemas</id>-->\n            <!--<phase>generate-sources</phase>-->\n            <!--<goals>-->\n              <!--<goal>schema</goal>-->\n              <!--<goal>protocol</goal>-->\n              <!--<goal>idl-protocol</goal>-->\n            <!--</goals>-->\n            <!--<configuration>-->\n              <!--<stringType>String</stringType>-->\n              <!--<sourceDirectory>src/main/avro</sourceDirectory>-->\n              <!--<fieldVisibility>private</fieldVisibility>-->\n            <!--</configuration>-->\n          <!--</execution>-->\n          <execution>\n            <id>test-schemas</id>\n            <phase>generate-test-sources</phase>\n            <goals>\n              <goal>schema</goal>\n              <goal>protocol</goal>\n              <goal>idl-protocol</goal>\n            </goals>\n            <configuration>\n              <stringType>String</stringType>\n              <sourceDirectory>src/test/avro</sourceDirectory>\n              <fieldVisibility>private</fieldVisibility>\n            </configuration>\n          </execution>\n        </executions>\n      </plugin>\n\n    </plugins>\n  </build>\n</project>\n"
  },
  {
    "path": "examples/apache-flink/src/test/avro/Record.avdl",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n@namespace(\"nl.basjes.parse.webevents\")\n\nprotocol FlinkTestRecord {\n\n  record Device {\n    long   screenWidth    ; // \"SCREENWIDTH:request.firstline.uri.query.s.width\"\n    long   screenHeight   ; // \"SCREENHEIGHT:request.firstline.uri.query.s.height\"\n  }\n\n  record Browser {\n    string useragent ; // \"STRING:request.user-agent\"\n  }\n\n  record ISP {\n    string asnNumber ;          // ASN:connection.client.host.asn.number\n    string asnOrganization ;    // STRING:connection.client.host.asn.organization\n    string ispName ;            // STRING:connection.client.host.isp.name\n    string ispOrganization ;    // STRING:connection.client.host.isp.organization\n  }\n\n  record GeoLocation {\n    string continentName;       // STRING:connection.client.host.continent.name\n    string continentCode;       // STRING:connection.client.host.continent.code\n    string countryName;         // STRING:connection.client.host.country.name\n    string countryIso;          // STRING:connection.client.host.country.iso\n    string subdivisionName;     // STRING:connection.client.host.subdivision.name\n    string subdivisionIso;      // STRING:connection.client.host.subdivision.iso\n    string cityName;            // STRING:connection.client.host.city.name\n    string postalCode;          // STRING:connection.client.host.postal.code\n    double locationLatitude;    // STRING:connection.client.host.location.latitude\n    double locationLongitude;   // STRING:connection.client.host.location.longitude\n  }\n\n  record Visitor {\n    string ip; // \"IP:connection.client.host\"\n    ISP isp;\n    GeoLocation geoLocation;\n  }\n\n  record Click {\n    long timestamp; // \"TIME.EPOCH:request.receive.time.epoch\"\n    Device  device;\n    Browser browser;\n    Visitor visitor;\n  }\n}\n"
  },
  {
    "path": "examples/apache-flink/src/test/java/nl/basjes/parse/httpdlog/flink/TestCase.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage nl.basjes.parse.httpdlog.flink;\n\nimport nl.basjes.parse.core.Parser;\nimport nl.basjes.parse.httpdlog.HttpdLoglineParser;\nimport nl.basjes.parse.httpdlog.dissectors.geoip.GeoIPCityDissector;\nimport nl.basjes.parse.httpdlog.dissectors.geoip.GeoIPISPDissector;\nimport nl.basjes.parse.httpdlog.flink.pojo.MyRecord;\n\n// CHECKSTYLE.OFF: LineLength\n// CHECKSTYLE.OFF: LeftCurly\n// CHECKSTYLE.OFF: ParamPad\n// CHECKSTYLE.OFF: HideUtilityClassConstructor\n\npublic final class TestCase {\n\n    private static final String TEST_MMDB_BASE_DIR = \"../../GeoIP2-TestData/test-data/\";\n    public static final String ISP_TEST_MMDB = TEST_MMDB_BASE_DIR + \"GeoIP2-ISP-Test.mmdb\";\n    public static final String CITY_TEST_MMDB = TEST_MMDB_BASE_DIR + \"GeoIP2-City-Test.mmdb\";\n\n    public static String getLogFormat() {\n        return \"%h %l %u %t \\\"%r\\\" %>s %b \\\"%{Referer}i\\\" \\\"%{User-Agent}i\\\" \\\"%{Cookie}i\\\"\";\n    }\n\n    public static String getInputLine() {\n        return \"2001:980:91c0:1:8d31:a232:25e5:85d - - [05/Sep/2010:11:27:50 +0200] \\\"GET /b/ss/advbolprod2/1/H.22.1/s73176445413647?AQB=1&pccr=true&vidn=27F07A1B85012045-4000011500517C43&&ndh=1&t=19%2F5%2F2012%2023%3A51%3A27%202%20-120&ce=UTF-8&ns=bol&pageName=%2Fnl%2Fp%2Ffissler-speciaal-pannen-grillpan-28-x-28-cm%2F9200000002876066%2F&g=http%3A%2F%2Fwww.bol.com%2Fnl%2Fp%2Ffissler-speciaal-pannen-grillpan-28-x-28-cm%2F9200000002876066%2F%3Fpromo%3Dkoken-pannen_303_hs-koken-pannen-afj-120601_B3_product_1_9200000002876066%26bltg.pg_nm%3Dkoken-pannen%26bltg.slt_id%3D303%26bltg.slt_nm%3Dhs-koken-pannen-afj-120601%26bltg.slt_p&r=http%3A%2F%2Fwww.bol.com%2Fnl%2Fm%2Fkoken-tafelen%2Fkoken-pannen%2FN%2F11766%2Findex.html%3Fblabla%3Dblablawashere&cc=EUR&ch=D%3Dv3&server=ps316&events=prodView%2Cevent1%2Cevent2%2Cevent31&products=%3B9200000002876066%3B%3B%3B%3Bevar3%3Dkth%7Cevar8%3D9200000002876066_Fissler%20Speciaal%20Pannen%20-%20Grillpan%20-%2028%20x%2028%20cm%7Cevar35%3D170%7Cevar47%3DKTH%7Cevar9%3DNew%7Cevar40%3Dno%20reviews%2C%3B%3B%3B%3Bevent31%3D423&c1=catalog%3Akth%3Aproduct-detail&v1=D%3Dc1&h1=catalog%2Fkth%2Fproduct-detail&h2=D%3DpageName&v3=kth&l3=endeca_001-mensen_default%2Cendeca_exact-boeken_default%2Cendeca_verschijningsjaar_default%2Cendeca_hardgoodscategoriesyn_default%2Cendeca_searchrank-hadoop_default%2Cendeca_genre_default%2Cendeca_uitvoering_default&v4=ps316&v6=koken-pannen_303_hs-koken-pannen-afj-120601_B3_product_1_9200000002876066&v10=Tu%2023%3A30&v12=logged%20in&v13=New&c25=niet%20ssl&c26=3631&c30=84.106.227.113.1323208998208762&v31=2000285551&c45=20120619235127&c46=20120501%204.3.4.1&c47=D%3Ds_vi&c49=%2Fnl%2Fcatalog%2Fproduct-detail.jsp&c50=%2Fnl%2Fcatalog%2Fproduct-detail.jsp&v51=www.bol.com&s=1280x800&c=24&j=1.7&v=N&k=Y&bw=1280&bh=272&p=Shockwave%20Flash%3B&AQE=1 HTTP/1.1\\\" 200 23617 \\\"http://www.google.nl/imgres?imgurl=http://daniel_en_sander.basjes.nl/fotos/geboorte-kaartje/geboortekaartje-binnenkant.jpg&imgrefurl=http://daniel_en_sander.basjes.nl/fotos/geboorte-kaartje&usg=__LDxRMkacRs6yLluLcIrwoFsXY6o=&h=521&w=1024&sz=41&hl=nl&start=13&zoom=1&um=1&itbs=1&tbnid=Sqml3uGbjoyBYM:&tbnh=76&tbnw=150&prev=/images%3Fq%3Dbinnenkant%2Bgeboortekaartje%26um%3D1%26hl%3Dnl%26sa%3DN%26biw%3D1882%26bih%3D1014%26tbs%3Disch:1\\\" \\\"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_4; nl-nl) AppleWebKit/533.17.8 (KHTML, like Gecko) Version/5.0.1 Safari/533.17.8\\\" \\\"jquery-ui-theme=Eggplant; BuI=SomeThing; Apache=127.0.0.1.1351111543699529\\\"\";\n    }\n\n    public static Parser<MyRecord> createTestParser() throws NoSuchMethodException {\n        Parser<MyRecord> parser = new HttpdLoglineParser<>(MyRecord.class, getLogFormat());\n\n        parser.addDissector(new nl.basjes.parse.httpdlog.dissectors.ScreenResolutionDissector());\n\n        parser.addTypeRemapping(\"request.firstline.uri.query.g\", \"HTTP.URI\");\n        parser.addTypeRemapping(\"request.firstline.uri.query.r\", \"HTTP.URI\");\n        parser.addTypeRemapping(\"request.firstline.uri.query.s\", \"SCREENRESOLUTION\");\n\n        parser.addParseTarget(\"setConnectionClientHost\", \"IP:connection.client.host\");\n        parser.addParseTarget(\"setRequestReceiveTime\",   \"TIME.STAMP:request.receive.time\");\n        parser.addParseTarget(\"setReferrer\",             \"STRING:request.firstline.uri.query.g.query.promo\");\n        parser.addParseTarget(\"setScreenResolution\",     \"STRING:request.firstline.uri.query.s\");\n        parser.addParseTarget(\"setScreenWidth\",          \"SCREENWIDTH:request.firstline.uri.query.s.width\");\n        parser.addParseTarget(\"setScreenHeight\",         \"SCREENHEIGHT:request.firstline.uri.query.s.height\");\n        parser.addParseTarget(\"setGoogleQuery\",          \"STRING:request.firstline.uri.query.r.query.blabla\");\n        parser.addParseTarget(\"setBui\",                  \"HTTP.COOKIE:request.cookies.bui\");\n        parser.addParseTarget(\"setUseragent\",            \"HTTP.USERAGENT:request.user-agent\");\n\n        parser.addDissector(new GeoIPISPDissector(ISP_TEST_MMDB));\n        parser.addParseTarget(\"setAsnNumber\",            \"ASN:connection.client.host.asn.number\");\n        parser.addParseTarget(\"setAsnOrganization\",      \"STRING:connection.client.host.asn.organization\");\n        parser.addParseTarget(\"setIspName\",              \"STRING:connection.client.host.isp.name\");\n        parser.addParseTarget(\"setIspOrganization\",      \"STRING:connection.client.host.isp.organization\");\n\n        parser.addDissector(new GeoIPCityDissector(CITY_TEST_MMDB));\n        parser.addParseTarget(\"setContinentName\",        \"STRING:connection.client.host.continent.name\");\n        parser.addParseTarget(\"setContinentCode\",        \"STRING:connection.client.host.continent.code\");\n        parser.addParseTarget(\"setCountryName\",          \"STRING:connection.client.host.country.name\");\n        parser.addParseTarget(\"setCountryIso\",           \"STRING:connection.client.host.country.iso\");\n        parser.addParseTarget(\"setSubdivisionName\",      \"STRING:connection.client.host.subdivision.name\");\n        parser.addParseTarget(\"setSubdivisionIso\",       \"STRING:connection.client.host.subdivision.iso\");\n        parser.addParseTarget(\"setCityName\",             \"STRING:connection.client.host.city.name\");\n        parser.addParseTarget(\"setPostalCode\",           \"STRING:connection.client.host.postal.code\");\n        parser.addParseTarget(\"setLocationLatitude\",     \"STRING:connection.client.host.location.latitude\");\n        parser.addParseTarget(\"setLocationLongitude\",    \"STRING:connection.client.host.location.longitude\");\n\n        return parser;\n    }\n\n    public static String getExpectedConnectionClientHost()      { return \"2001:980:91c0:1:8d31:a232:25e5:85d\"; }\n    public static String getExpectedRequestReceiveTime()        { return \"05/Sep/2010:11:27:50 +0200\"; }\n    public static Long   getExpectedRequestReceiveTimeEpoch()   { return 1283678870000L; }\n    public static String getExpectedReferrer()                  { return \"koken-pannen_303_hs-koken-pannen-afj-120601_B3_product_1_9200000002876066\"; }\n    public static String getExpectedScreenResolution()          { return \"1280x800\"; }\n    public static Long   getExpectedScreenWidth()               { return 1280L; }\n    public static Long   getExpectedScreenHeight()              { return 800L; }\n    public static String getExpectedGoogleQuery()               { return \"blablawashere\"; }\n    public static String getExpectedBui()                       { return \"SomeThing\"; }\n    public static String getExpectedUseragent()                 { return \"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_4; nl-nl) AppleWebKit/533.17.8 (KHTML, like Gecko) Version/5.0.1 Safari/533.17.8\"; }\n\n    public static String getExpectedAsnNumber()                 { return \"6666\"; }\n    public static String getExpectedAsnOrganization()           { return \"Basjes Global Network IPv6\"; }\n    public static String getExpectedIspName()                   { return \"Basjes ISP IPv6\"; }\n    public static String getExpectedIspOrganization()           { return \"Niels Basjes IPv6\"; }\n\n    public static String getExpectedContinentName()             { return \"Europe\"; }\n    public static String getExpectedContinentCode()             { return \"EU\"; }\n    public static String getExpectedCountryName()               { return \"Netherlands\"; }\n    public static String getExpectedCountryIso()                { return \"NL\"; }\n    public static String getExpectedSubdivisionName()           { return \"Noord Holland\"; }\n    public static String getExpectedSubdivisionIso()            { return \"NH\"; }\n    public static String getExpectedCityName()                  { return \"Amstelveen\"; }\n    public static String getExpectedPostalCode()                { return \"1187\"; }\n    public static Double getExpectedLocationLatitude()          { return 52.5; }\n    public static Double getExpectedLocationLongitude()         { return 5.75; }\n\n}\n"
  },
  {
    "path": "examples/apache-flink/src/test/java/nl/basjes/parse/httpdlog/flink/avro/ExpectedClick.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.httpdlog.flink.avro;\n\nimport nl.basjes.parse.webevents.Click;\n\nimport static nl.basjes.parse.httpdlog.flink.TestCase.getExpectedAsnNumber;\nimport static nl.basjes.parse.httpdlog.flink.TestCase.getExpectedAsnOrganization;\nimport static nl.basjes.parse.httpdlog.flink.TestCase.getExpectedCityName;\nimport static nl.basjes.parse.httpdlog.flink.TestCase.getExpectedConnectionClientHost;\nimport static nl.basjes.parse.httpdlog.flink.TestCase.getExpectedContinentCode;\nimport static nl.basjes.parse.httpdlog.flink.TestCase.getExpectedContinentName;\nimport static nl.basjes.parse.httpdlog.flink.TestCase.getExpectedCountryIso;\nimport static nl.basjes.parse.httpdlog.flink.TestCase.getExpectedCountryName;\nimport static nl.basjes.parse.httpdlog.flink.TestCase.getExpectedIspName;\nimport static nl.basjes.parse.httpdlog.flink.TestCase.getExpectedIspOrganization;\nimport static nl.basjes.parse.httpdlog.flink.TestCase.getExpectedLocationLatitude;\nimport static nl.basjes.parse.httpdlog.flink.TestCase.getExpectedLocationLongitude;\nimport static nl.basjes.parse.httpdlog.flink.TestCase.getExpectedPostalCode;\nimport static nl.basjes.parse.httpdlog.flink.TestCase.getExpectedRequestReceiveTimeEpoch;\nimport static nl.basjes.parse.httpdlog.flink.TestCase.getExpectedScreenHeight;\nimport static nl.basjes.parse.httpdlog.flink.TestCase.getExpectedScreenWidth;\nimport static nl.basjes.parse.httpdlog.flink.TestCase.getExpectedSubdivisionIso;\nimport static nl.basjes.parse.httpdlog.flink.TestCase.getExpectedSubdivisionName;\nimport static nl.basjes.parse.httpdlog.flink.TestCase.getExpectedUseragent;\n\n// CHECKSTYLE.OFF: HideUtilityClassConstructor\npublic class ExpectedClick {\n\n    public static Click create(){\n        Click.Builder builder = Click.newBuilder();\n\n        builder\n            .setTimestamp(getExpectedRequestReceiveTimeEpoch())\n            .getDeviceBuilder()\n                .setScreenWidth(getExpectedScreenWidth())\n                .setScreenHeight(getExpectedScreenHeight());\n\n        builder\n            .getBrowserBuilder()\n                .setUseragent(getExpectedUseragent());\n\n        builder\n            .getVisitorBuilder()\n                .setIp(getExpectedConnectionClientHost());\n\n        builder\n            .getVisitorBuilder()\n                .getIspBuilder()\n                    .setAsnNumber(getExpectedAsnNumber())\n                    .setAsnOrganization(getExpectedAsnOrganization())\n                    .setIspName(getExpectedIspName())\n                    .setIspOrganization(getExpectedIspOrganization());\n\n        builder\n            .getVisitorBuilder()\n                .getGeoLocationBuilder()\n                    .setContinentName(getExpectedContinentName())\n                    .setContinentCode(getExpectedContinentCode())\n                    .setCountryName(getExpectedCountryName())\n                    .setCountryIso(getExpectedCountryIso())\n                    .setSubdivisionName(getExpectedSubdivisionName())\n                    .setSubdivisionIso(getExpectedSubdivisionIso())\n                    .setCityName(getExpectedCityName())\n                    .setPostalCode(getExpectedPostalCode())\n                    .setLocationLatitude(getExpectedLocationLatitude())\n                    .setLocationLongitude(getExpectedLocationLongitude());\n\n        return builder.build();\n    }\n\n}\n"
  },
  {
    "path": "examples/apache-flink/src/test/java/nl/basjes/parse/httpdlog/flink/avro/TestParserMapFunctionAvroClass.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage nl.basjes.parse.httpdlog.flink.avro;\n\nimport nl.basjes.parse.core.Field;\nimport nl.basjes.parse.core.Parser;\nimport nl.basjes.parse.httpdlog.HttpdLoglineParser;\nimport nl.basjes.parse.httpdlog.dissectors.ScreenResolutionDissector;\nimport nl.basjes.parse.httpdlog.dissectors.geoip.GeoIPCityDissector;\nimport nl.basjes.parse.httpdlog.dissectors.geoip.GeoIPISPDissector;\nimport nl.basjes.parse.httpdlog.flink.TestCase;\nimport nl.basjes.parse.webevents.Click;\nimport org.apache.commons.lang3.builder.Builder;\nimport org.apache.flink.api.common.functions.OpenContext;\nimport org.apache.flink.api.common.functions.RichMapFunction;\nimport org.apache.flink.streaming.api.datastream.DataStream;\nimport org.apache.flink.streaming.api.environment.LocalStreamEnvironment;\nimport org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;\nimport org.junit.jupiter.api.Test;\n\nimport java.io.Serializable;\nimport java.util.List;\n\nimport static nl.basjes.parse.httpdlog.flink.TestCase.CITY_TEST_MMDB;\nimport static nl.basjes.parse.httpdlog.flink.TestCase.ISP_TEST_MMDB;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\n// CHECKSTYLE.OFF: LineLength\n// CHECKSTYLE.OFF: LeftCurly\nclass TestParserMapFunctionAvroClass implements Serializable {\n\n    public static class ClickSetter implements Builder<Click> {\n\n        final Click.Builder builder = Click.newBuilder();\n\n        @Field(\"TIME.EPOCH:request.receive.time.epoch\")             public void setRequestReceiveTime(Long value)       { builder.setTimestamp(value);                          }\n\n        @Field(\"SCREENWIDTH:request.firstline.uri.query.s.width\")   public void setScreenWidth(Long value)              { builder.getDeviceBuilder().setScreenWidth(value);     }\n        @Field(\"SCREENHEIGHT:request.firstline.uri.query.s.height\") public void setScreenHeight(Long value)             { builder.getDeviceBuilder().setScreenHeight(value);    }\n\n        @Field(\"HTTP.USERAGENT:request.user-agent\")                 public void setUseragent(String value)              { builder.getBrowserBuilder().setUseragent(value);      }\n\n        @Field(\"IP:connection.client.host\")                         public void setConnectionClientHost(String value)   { builder.getVisitorBuilder().setIp(value);             }\n\n        @Field(\"ASN:connection.client.host.asn.number\")             public void setAsnNumber(String value)              { builder.getVisitorBuilder().getIspBuilder().setAsnNumber(value);  }\n        @Field(\"STRING:connection.client.host.asn.organization\")    public void setAsnOrganization(String value)        { builder.getVisitorBuilder().getIspBuilder().setAsnOrganization(value);  }\n        @Field(\"STRING:connection.client.host.isp.name\")            public void setIspName(String value)                { builder.getVisitorBuilder().getIspBuilder().setIspName(value);  }\n        @Field(\"STRING:connection.client.host.isp.organization\")    public void setIspOrganization(String value)        { builder.getVisitorBuilder().getIspBuilder().setIspOrganization(value);  }\n\n        @Field(\"STRING:connection.client.host.continent.name\")      public void setContinentName(String value)          { builder.getVisitorBuilder().getGeoLocationBuilder().setContinentName(value);  }\n        @Field(\"STRING:connection.client.host.continent.code\")      public void setContinentCode(String value)          { builder.getVisitorBuilder().getGeoLocationBuilder().setContinentCode(value);  }\n        @Field(\"STRING:connection.client.host.country.name\")        public void setCountryName(String value)            { builder.getVisitorBuilder().getGeoLocationBuilder().setCountryName(value);  }\n        @Field(\"STRING:connection.client.host.country.iso\")         public void setCountryIso(String value)             { builder.getVisitorBuilder().getGeoLocationBuilder().setCountryIso(value);  }\n        @Field(\"STRING:connection.client.host.subdivision.name\")    public void setSubdivisionName(String value)        { builder.getVisitorBuilder().getGeoLocationBuilder().setSubdivisionName(value);  }\n        @Field(\"STRING:connection.client.host.subdivision.iso\")     public void setSubdivisionIso(String value)         { builder.getVisitorBuilder().getGeoLocationBuilder().setSubdivisionIso(value);  }\n        @Field(\"STRING:connection.client.host.city.name\")           public void setCityName(String value)               { builder.getVisitorBuilder().getGeoLocationBuilder().setCityName(value);  }\n        @Field(\"STRING:connection.client.host.postal.code\")         public void setPostalCode(String value)             { builder.getVisitorBuilder().getGeoLocationBuilder().setPostalCode(value);  }\n        @Field(\"STRING:connection.client.host.location.latitude\")   public void setLocationLatitude(Double value)       { builder.getVisitorBuilder().getGeoLocationBuilder().setLocationLatitude(value);  }\n        @Field(\"STRING:connection.client.host.location.longitude\")  public void setLocationLongitude(Double value)      { builder.getVisitorBuilder().getGeoLocationBuilder().setLocationLongitude(value);  }\n\n        @Override\n        public Click build() {\n            return builder.build();\n        }\n    }\n\n    public static class MyParserMapper extends RichMapFunction<String, Click> {\n        private Parser<ClickSetter> parser;\n\n        @Override\n        public void open(OpenContext openContext) {\n            parser = new HttpdLoglineParser<>(ClickSetter.class, TestCase.getLogFormat())\n                .addDissector(new ScreenResolutionDissector())\n                .addTypeRemapping(\"request.firstline.uri.query.g\", \"HTTP.URI\")\n                .addTypeRemapping(\"request.firstline.uri.query.r\", \"HTTP.URI\")\n                .addTypeRemapping(\"request.firstline.uri.query.s\", \"SCREENRESOLUTION\")\n                .addDissector(new GeoIPISPDissector(ISP_TEST_MMDB))\n                .addDissector(new GeoIPCityDissector(CITY_TEST_MMDB));\n        }\n\n        @Override\n        public Click map(String input) throws Exception {\n            ClickSetter setter = parser.parse(new ClickSetter(), input);\n            if (setter == null) {\n                System.err.println(\"Something went terribly wrong\");\n                return null;\n            }\n            return setter.build();\n        }\n    }\n\n    @Test\n    void testClassDefinitionAvro() throws Exception {\n        // set up the execution environment\n        final StreamExecutionEnvironment env = LocalStreamEnvironment.getExecutionEnvironment();\n        env.setParallelism(1);\n        DataStream<String> input = env.fromData(TestCase.getInputLine());\n\n        DataStream<Click> filledTestRecords = input\n            .map(new MyParserMapper())\n            .name(\"Extract Elements from logline\");\n\n        filledTestRecords.print();\n\n        List<Click> result = filledTestRecords.executeAndCollect(100);\n\n        assertEquals(1, result.size());\n        assertEquals(ExpectedClick.create(), result.getFirst());\n    }\n\n}\n"
  },
  {
    "path": "examples/apache-flink/src/test/java/nl/basjes/parse/httpdlog/flink/avro/TestParserMapFunctionAvroInline.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage nl.basjes.parse.httpdlog.flink.avro;\n\nimport nl.basjes.parse.core.Field;\nimport nl.basjes.parse.core.Parser;\nimport nl.basjes.parse.httpdlog.HttpdLoglineParser;\nimport nl.basjes.parse.httpdlog.dissectors.ScreenResolutionDissector;\nimport nl.basjes.parse.httpdlog.dissectors.geoip.GeoIPCityDissector;\nimport nl.basjes.parse.httpdlog.dissectors.geoip.GeoIPISPDissector;\nimport nl.basjes.parse.httpdlog.flink.TestCase;\nimport nl.basjes.parse.webevents.Click;\nimport org.apache.commons.lang3.builder.Builder;\nimport org.apache.flink.api.common.functions.OpenContext;\nimport org.apache.flink.api.common.functions.RichMapFunction;\nimport org.apache.flink.streaming.api.datastream.DataStream;\nimport org.apache.flink.streaming.api.environment.LocalStreamEnvironment;\nimport org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;\nimport org.junit.jupiter.api.Test;\n\nimport java.io.Serializable;\nimport java.util.List;\n\nimport static nl.basjes.parse.httpdlog.flink.TestCase.CITY_TEST_MMDB;\nimport static nl.basjes.parse.httpdlog.flink.TestCase.ISP_TEST_MMDB;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\n// CHECKSTYLE.OFF: LineLength\n// CHECKSTYLE.OFF: LeftCurly\nclass TestParserMapFunctionAvroInline implements Serializable {\n\n    public static class ClickSetter implements Builder<Click> {\n\n        final Click.Builder builder = Click.newBuilder();\n\n        @Field(\"TIME.EPOCH:request.receive.time.epoch\")             public void setRequestReceiveTime(Long value)       { builder.setTimestamp(value);                          }\n\n        @Field(\"SCREENWIDTH:request.firstline.uri.query.s.width\")   public void setScreenWidth(Long value)              { builder.getDeviceBuilder().setScreenWidth(value);     }\n        @Field(\"SCREENHEIGHT:request.firstline.uri.query.s.height\") public void setScreenHeight(Long value)             { builder.getDeviceBuilder().setScreenHeight(value);    }\n\n        @Field(\"HTTP.USERAGENT:request.user-agent\")                 public void setUseragent(String value)              { builder.getBrowserBuilder().setUseragent(value);      }\n\n        @Field(\"IP:connection.client.host\")                         public void setConnectionClientHost(String value)   { builder.getVisitorBuilder().setIp(value);             }\n\n        @Field(\"ASN:connection.client.host.asn.number\")             public void setAsnNumber(String value)              { builder.getVisitorBuilder().getIspBuilder().setAsnNumber(value);  }\n        @Field(\"STRING:connection.client.host.asn.organization\")    public void setAsnOrganization(String value)        { builder.getVisitorBuilder().getIspBuilder().setAsnOrganization(value);  }\n        @Field(\"STRING:connection.client.host.isp.name\")            public void setIspName(String value)                { builder.getVisitorBuilder().getIspBuilder().setIspName(value);  }\n        @Field(\"STRING:connection.client.host.isp.organization\")    public void setIspOrganization(String value)        { builder.getVisitorBuilder().getIspBuilder().setIspOrganization(value);  }\n\n        @Field(\"STRING:connection.client.host.continent.name\")      public void setContinentName(String value)          { builder.getVisitorBuilder().getGeoLocationBuilder().setContinentName(value);  }\n        @Field(\"STRING:connection.client.host.continent.code\")      public void setContinentCode(String value)          { builder.getVisitorBuilder().getGeoLocationBuilder().setContinentCode(value);  }\n        @Field(\"STRING:connection.client.host.country.name\")        public void setCountryName(String value)            { builder.getVisitorBuilder().getGeoLocationBuilder().setCountryName(value);  }\n        @Field(\"STRING:connection.client.host.country.iso\")         public void setCountryIso(String value)             { builder.getVisitorBuilder().getGeoLocationBuilder().setCountryIso(value);  }\n        @Field(\"STRING:connection.client.host.subdivision.name\")    public void setSubdivisionName(String value)        { builder.getVisitorBuilder().getGeoLocationBuilder().setSubdivisionName(value);  }\n        @Field(\"STRING:connection.client.host.subdivision.iso\")     public void setSubdivisionIso(String value)         { builder.getVisitorBuilder().getGeoLocationBuilder().setSubdivisionIso(value);  }\n        @Field(\"STRING:connection.client.host.city.name\")           public void setCityName(String value)               { builder.getVisitorBuilder().getGeoLocationBuilder().setCityName(value);  }\n        @Field(\"STRING:connection.client.host.postal.code\")         public void setPostalCode(String value)             { builder.getVisitorBuilder().getGeoLocationBuilder().setPostalCode(value);  }\n        @Field(\"STRING:connection.client.host.location.latitude\")   public void setLocationLatitude(Double value)       { builder.getVisitorBuilder().getGeoLocationBuilder().setLocationLatitude(value);  }\n        @Field(\"STRING:connection.client.host.location.longitude\")  public void setLocationLongitude(Double value)      { builder.getVisitorBuilder().getGeoLocationBuilder().setLocationLongitude(value);  }\n\n        @Override\n        public Click build() {\n            return builder.build();\n        }\n    }\n\n    @Test\n    void testInlineDefinitionAvro() throws Exception {\n        // set up the execution environment\n        final StreamExecutionEnvironment env = LocalStreamEnvironment.getExecutionEnvironment();\n        env.setParallelism(1);\n\n        DataStream<String> input = env.fromData(TestCase.getInputLine());\n\n        DataStream<Click> filledTestRecords = input\n            .map(new RichMapFunction<String, Click>() {\n                private Parser<ClickSetter> parser;\n\n                @Override\n                public void open(OpenContext openContext) {\n                    parser = new HttpdLoglineParser<>(ClickSetter.class, TestCase.getLogFormat())\n                        .addDissector(new ScreenResolutionDissector())\n                        .addTypeRemapping(\"request.firstline.uri.query.g\", \"HTTP.URI\")\n                        .addTypeRemapping(\"request.firstline.uri.query.r\", \"HTTP.URI\")\n                        .addTypeRemapping(\"request.firstline.uri.query.s\", \"SCREENRESOLUTION\")\n                        .addDissector(new GeoIPISPDissector(ISP_TEST_MMDB))\n                        .addDissector(new GeoIPCityDissector(CITY_TEST_MMDB));\n                }\n\n                @Override\n                public Click map(String line) throws Exception {\n                    return parser.parse(line).build();\n                }\n            }).name(\"Extract Elements from logline\");\n\n        filledTestRecords.print();\n\n        List<Click> result = filledTestRecords.executeAndCollect(100);\n\n        assertEquals(1, result.size());\n        assertEquals(ExpectedClick.create(), result.getFirst());\n    }\n\n}\n"
  },
  {
    "path": "examples/apache-flink/src/test/java/nl/basjes/parse/httpdlog/flink/pojo/MyRecord.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage nl.basjes.parse.httpdlog.flink.pojo;\n\nimport lombok.EqualsAndHashCode;\nimport lombok.Getter;\nimport lombok.Setter;\nimport lombok.ToString;\nimport org.junit.jupiter.api.Test;\n\nimport java.io.Serializable;\n\nimport static nl.basjes.parse.httpdlog.flink.TestCase.getExpectedAsnNumber;\nimport static nl.basjes.parse.httpdlog.flink.TestCase.getExpectedAsnOrganization;\nimport static nl.basjes.parse.httpdlog.flink.TestCase.getExpectedBui;\nimport static nl.basjes.parse.httpdlog.flink.TestCase.getExpectedCityName;\nimport static nl.basjes.parse.httpdlog.flink.TestCase.getExpectedConnectionClientHost;\nimport static nl.basjes.parse.httpdlog.flink.TestCase.getExpectedContinentCode;\nimport static nl.basjes.parse.httpdlog.flink.TestCase.getExpectedContinentName;\nimport static nl.basjes.parse.httpdlog.flink.TestCase.getExpectedCountryIso;\nimport static nl.basjes.parse.httpdlog.flink.TestCase.getExpectedCountryName;\nimport static nl.basjes.parse.httpdlog.flink.TestCase.getExpectedGoogleQuery;\nimport static nl.basjes.parse.httpdlog.flink.TestCase.getExpectedIspName;\nimport static nl.basjes.parse.httpdlog.flink.TestCase.getExpectedIspOrganization;\nimport static nl.basjes.parse.httpdlog.flink.TestCase.getExpectedLocationLatitude;\nimport static nl.basjes.parse.httpdlog.flink.TestCase.getExpectedLocationLongitude;\nimport static nl.basjes.parse.httpdlog.flink.TestCase.getExpectedPostalCode;\nimport static nl.basjes.parse.httpdlog.flink.TestCase.getExpectedReferrer;\nimport static nl.basjes.parse.httpdlog.flink.TestCase.getExpectedRequestReceiveTime;\nimport static nl.basjes.parse.httpdlog.flink.TestCase.getExpectedScreenHeight;\nimport static nl.basjes.parse.httpdlog.flink.TestCase.getExpectedScreenResolution;\nimport static nl.basjes.parse.httpdlog.flink.TestCase.getExpectedScreenWidth;\nimport static nl.basjes.parse.httpdlog.flink.TestCase.getExpectedSubdivisionIso;\nimport static nl.basjes.parse.httpdlog.flink.TestCase.getExpectedSubdivisionName;\nimport static nl.basjes.parse.httpdlog.flink.TestCase.getExpectedUseragent;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\n\n// CHECKSTYLE.OFF: ParamPad\n@ToString\n@EqualsAndHashCode\npublic class MyRecord implements Serializable {\n\n    @Getter @Setter private String connectionClientHost = null;\n    @Getter @Setter private String requestReceiveTime   = null;\n    @Getter @Setter private String referrer             = null;\n    @Getter @Setter private String screenResolution     = null;\n    @Getter @Setter private Long   screenWidth          = null;\n    @Getter @Setter private Long   screenHeight         = null;\n    @Getter @Setter private String googleQuery          = null;\n    @Getter @Setter private String bui                  = null;\n    @Getter @Setter private String useragent            = null;\n\n    @Getter @Setter private String asnNumber            = null;\n    @Getter @Setter private String asnOrganization      = null;\n    @Getter @Setter private String ispName              = null;\n    @Getter @Setter private String ispOrganization      = null;\n\n    @Getter @Setter private String continentName        = null;\n    @Getter @Setter private String continentCode        = null;\n    @Getter @Setter private String countryName          = null;\n    @Getter @Setter private String countryIso           = null;\n    @Getter @Setter private String subdivisionName      = null;\n    @Getter @Setter private String subdivisionIso       = null;\n    @Getter @Setter private String cityName             = null;\n    @Getter @Setter private String postalCode           = null;\n    @Getter @Setter private Double locationLatitude     = null;\n    @Getter @Setter private Double locationLongitude    = null;\n\n    public void assertIsValid() {\n        assertEquals(getExpectedConnectionClientHost(), getConnectionClientHost());\n        assertEquals(getExpectedRequestReceiveTime(),   getRequestReceiveTime());\n        assertEquals(getExpectedReferrer(),             getReferrer());\n        assertEquals(getExpectedScreenResolution(),     getScreenResolution());\n        assertEquals(getExpectedScreenWidth(),          getScreenWidth());\n        assertEquals(getExpectedScreenHeight(),         getScreenHeight());\n        assertEquals(getExpectedGoogleQuery(),          getGoogleQuery());\n        assertEquals(getExpectedBui(),                  getBui());\n        assertEquals(getExpectedUseragent(),            getUseragent());\n\n        assertEquals(getExpectedAsnNumber(),            getAsnNumber());\n        assertEquals(getExpectedAsnOrganization(),      getAsnOrganization());\n        assertEquals(getExpectedIspName(),              getIspName());\n        assertEquals(getExpectedIspOrganization(),      getIspOrganization());\n\n        assertEquals(getExpectedContinentName(),        getContinentName());\n        assertEquals(getExpectedContinentCode(),        getContinentCode());\n        assertEquals(getExpectedSubdivisionName(),      getSubdivisionName());\n        assertEquals(getExpectedSubdivisionIso(),       getSubdivisionIso());\n        assertEquals(getExpectedCountryName(),          getCountryName());\n        assertEquals(getExpectedCountryIso(),           getCountryIso());\n        assertEquals(getExpectedCityName(),             getCityName());\n        assertEquals(getExpectedPostalCode(),           getPostalCode());\n        assertEquals(getExpectedLocationLatitude(),     getLocationLatitude());\n        assertEquals(getExpectedLocationLongitude(),    getLocationLongitude());\n    }\n\n    public MyRecord setFullValid() {\n        setConnectionClientHost (getExpectedConnectionClientHost());\n        setRequestReceiveTime   (getExpectedRequestReceiveTime());\n        setReferrer             (getExpectedReferrer());\n        setScreenResolution     (getExpectedScreenResolution());\n        setScreenWidth          (getExpectedScreenWidth());\n        setScreenHeight         (getExpectedScreenHeight());\n        setGoogleQuery          (getExpectedGoogleQuery());\n        setBui                  (getExpectedBui());\n        setUseragent            (getExpectedUseragent());\n\n        setAsnNumber            (getExpectedAsnNumber());\n        setAsnOrganization      (getExpectedAsnOrganization());\n        setIspName              (getExpectedIspName());\n        setIspOrganization      (getExpectedIspOrganization());\n\n        setContinentName        (getExpectedContinentName());\n        setContinentCode        (getExpectedContinentCode());\n        setCountryName          (getExpectedCountryName());\n        setCountryIso           (getExpectedCountryIso());\n        setSubdivisionName      (getExpectedSubdivisionName());\n        setSubdivisionIso       (getExpectedSubdivisionIso());\n        setCityName             (getExpectedCityName());\n        setPostalCode           (getExpectedPostalCode());\n        setLocationLatitude     (getExpectedLocationLatitude());\n        setLocationLongitude    (getExpectedLocationLongitude());\n\n        return this;\n    }\n\n\n    @Test\n    void checkTestMethodsPass() {\n        MyRecord testRecord = new MyRecord().setFullValid();\n        testRecord.assertIsValid();\n    }\n\n    @Test\n    void checkTestMethodsFail() {\n        assertThrows(AssertionError.class, () -> {\n            MyRecord testRecord = new MyRecord();\n            testRecord.assertIsValid();\n        });\n    }\n\n}\n"
  },
  {
    "path": "examples/apache-flink/src/test/java/nl/basjes/parse/httpdlog/flink/pojo/TestParserMapFunctionClass.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage nl.basjes.parse.httpdlog.flink.pojo;\n\nimport nl.basjes.parse.core.Parser;\nimport nl.basjes.parse.httpdlog.flink.TestCase;\nimport org.apache.flink.api.common.functions.OpenContext;\nimport org.apache.flink.api.common.functions.RichMapFunction;\nimport org.apache.flink.streaming.api.datastream.DataStream;\nimport org.apache.flink.streaming.api.environment.LocalStreamEnvironment;\nimport org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;\nimport org.junit.jupiter.api.Test;\n\nimport java.io.Serializable;\nimport java.util.List;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\nclass TestParserMapFunctionClass implements Serializable {\n\n    public static class MyParserMapper extends RichMapFunction<String, MyRecord> {\n        private Parser<MyRecord> parser;\n\n        @Override\n        public void open(OpenContext openContext) throws Exception {\n            parser = TestCase.createTestParser();\n        }\n\n        @Override\n        public MyRecord map(String line) throws Exception {\n            return parser.parse(line);\n        }\n    }\n\n    @Test\n    void testClassDefinition() throws Exception {\n        // set up the execution environment\n        final StreamExecutionEnvironment env = LocalStreamEnvironment.getExecutionEnvironment();\n        env.setParallelism(1);\n        DataStream<String> input = env.fromData(TestCase.getInputLine());\n\n        DataStream<MyRecord> filledTestRecords = input\n            .map(new MyParserMapper())\n            .name(\"Extract Elements from logline\");\n\n        filledTestRecords.print();\n\n        List<MyRecord> result = filledTestRecords.executeAndCollect(100);\n\n        assertEquals(1, result.size());\n        assertEquals(new MyRecord().setFullValid(), result.getFirst());\n    }\n\n}\n"
  },
  {
    "path": "examples/apache-flink/src/test/java/nl/basjes/parse/httpdlog/flink/pojo/TestParserMapFunctionInline.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage nl.basjes.parse.httpdlog.flink.pojo;\n\nimport nl.basjes.parse.core.Parser;\nimport nl.basjes.parse.httpdlog.flink.TestCase;\nimport org.apache.flink.api.common.functions.OpenContext;\nimport org.apache.flink.api.common.functions.RichMapFunction;\nimport org.apache.flink.streaming.api.datastream.DataStream;\nimport org.apache.flink.streaming.api.environment.LocalStreamEnvironment;\nimport org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;\nimport org.junit.jupiter.api.Test;\n\nimport java.io.Serializable;\nimport java.util.List;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\nclass TestParserMapFunctionInline implements Serializable {\n\n    @Test\n    void testInlineDefinition() throws Exception {\n        // set up the execution environment\n        final StreamExecutionEnvironment env = LocalStreamEnvironment.getExecutionEnvironment();\n        env.setParallelism(1);\n\n        DataStream<String> input = env.fromData(TestCase.getInputLine());\n\n        DataStream<MyRecord> filledTestRecords = input\n            .map(new RichMapFunction<String, MyRecord>() {\n                private Parser<MyRecord> parser;\n\n                @Override\n                public void open(OpenContext openContext) throws Exception {\n                    parser = TestCase.createTestParser();\n                }\n\n                @Override\n                public MyRecord map(String line) throws Exception {\n                    return parser.parse(line);\n                }\n            }).name(\"Extract Elements from logline\");\n\n        filledTestRecords.print();\n\n        List<MyRecord> result = filledTestRecords.executeAndCollect(100);\n\n        assertEquals(1, result.size());\n        assertEquals(new MyRecord().setFullValid(), result.getFirst());\n    }\n\n}\n"
  },
  {
    "path": "examples/apache-flink/src/test/resources/log4j.properties",
    "content": "#\n# Apache HTTPD & NGINX Access log parsing made easy\n# Copyright (C) 2011-2023 Niels Basjes\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# Root logger option\nlog4j.rootLogger=DEBUG, stdout\n#, file\nlog4j.appender.stdout=org.apache.log4j.ConsoleAppender\nlog4j.appender.stdout.Target=System.out\nlog4j.appender.stdout.threshold=INFO\nlog4j.appender.stdout.layout=org.apache.log4j.PatternLayout\nlog4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} [%-5p] %-40c{1}:%5L: %m%n\n## file appender\n#log4j.appender.file=org.apache.log4j.RollingFileAppender\n#log4j.appender.file.File=target/debug.log\n#log4j.appender.file.threshold=DEBUG\n#log4j.appender.file.layout=org.apache.log4j.PatternLayout\n#log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd} %d{ABSOLUTE} [%-5p] %-40c{1}:%5L: %m%n\n#log4j.appender.file.Append=false\n"
  },
  {
    "path": "examples/apache-hadoop-mapreduce/.gitignore",
    "content": "debug-output\noutput\n"
  },
  {
    "path": "examples/apache-hadoop-mapreduce/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n Apache HTTPD & NGINX Access log parsing made easy\n Copyright (C) 2011-2023 Niels Basjes\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n https://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n-->\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n  <modelVersion>4.0.0</modelVersion>\n  <parent>\n    <artifactId>httpdlog-examples</artifactId>\n    <groupId>nl.basjes.parse.httpdlog.examples</groupId>\n    <version>6.0.1-SNAPSHOT</version>\n  </parent>\n  <artifactId>apache-hadoop-mapreduce</artifactId>\n  <name>Parser - Examples - Apache Hadoop MapReduce</name>\n\n  <properties>\n    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n    <mainClass>nl.basjes.hadoop.io.input.Wordcount</mainClass>\n\n      <!-- The Hadoop dependencies are too hard to make this check pass -->\n      <depencency-convergence.phase>none</depencency-convergence.phase>\n  </properties>\n\n  <dependencies>\n    <dependency>\n      <groupId>org.apache.hadoop</groupId>\n      <artifactId>hadoop-client</artifactId>\n      <version>${hadoop.version}</version>\n      <scope>provided</scope>\n      <exclusions>\n        <exclusion>\n          <groupId>org.apache.commons</groupId>\n          <artifactId>commons-text</artifactId>\n        </exclusion>\n      </exclusions>\n    </dependency>\n\n    <dependency>\n      <groupId>nl.basjes.parse.httpdlog</groupId>\n      <version>${project.version}</version>\n      <artifactId>httpdlog-inputformat</artifactId>\n    </dependency>\n  </dependencies>\n\n  <build>\n    <plugins>\n      <plugin>\n        <groupId>org.apache.maven.plugins</groupId>\n        <artifactId>maven-assembly-plugin</artifactId>\n        <executions>\n          <execution>\n            <id>make-super-jar</id>\n            <phase>package</phase>\n            <goals>\n              <goal>single</goal>\n            </goals>\n            <configuration>\n              <descriptors>\n                <descriptor>src/main/assembly/job.xml</descriptor>\n              </descriptors>\n              <archive>\n                <addMavenDescriptor>true</addMavenDescriptor>\n                <manifest>\n                  <mainClass>${mainClass}</mainClass>\n                </manifest>\n              </archive>\n            </configuration>\n          </execution>\n        </executions>\n      </plugin>\n\n      <plugin>\n        <groupId>org.apache.maven.plugins</groupId>\n        <artifactId>maven-deploy-plugin</artifactId>\n        <configuration>\n          <skip>true</skip>\n        </configuration>\n      </plugin>\n\n    </plugins>\n  </build>\n</project>\n"
  },
  {
    "path": "examples/apache-hadoop-mapreduce/src/main/assembly/job.xml",
    "content": "<!--\n Apache HTTPD & NGINX Access log parsing made easy\n Copyright (C) 2011-2023 Niels Basjes\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n https://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n-->\n<assembly\n  xmlns=\"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2\"\n  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n  xsi:schemaLocation=\"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd\">\n  <id>job</id>\n  <formats>\n    <format>jar</format>\n  </formats>\n  <includeBaseDirectory>false</includeBaseDirectory>\n  <dependencySets>\n    <dependencySet>\n      <useProjectArtifact>false</useProjectArtifact>\n      <outputDirectory>lib</outputDirectory>\n      <unpack>false</unpack>\n      <scope>compile</scope>\n    </dependencySet>\n    <!--\n    <dependencySet>\n      <useProjectArtifact>false</useProjectArtifact>\n      <outputDirectory>lib</outputDirectory>\n      <unpack>false</unpack>\n      <scope>provided</scope>\n    </dependencySet>\n    -->\n  </dependencySets>\n  <fileSets>\n    <fileSet>\n      <directory>${project.build.outputDirectory}</directory>\n      <outputDirectory>${file.separator}</outputDirectory>\n    </fileSet>\n  </fileSets>\n</assembly>\n"
  },
  {
    "path": "examples/apache-hadoop-mapreduce/src/main/java/nl/basjes/hadoop/io/input/Wordcount.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.hadoop.io.input;\n\nimport nl.basjes.hadoop.input.ApacheHttpdLogfileInputFormat;\nimport org.apache.hadoop.conf.Configuration;\nimport org.apache.hadoop.conf.Configured;\nimport org.apache.hadoop.fs.FileSystem;\nimport org.apache.hadoop.fs.Path;\nimport org.apache.hadoop.io.LongWritable;\nimport org.apache.hadoop.io.MapWritable;\nimport org.apache.hadoop.io.Text;\nimport org.apache.hadoop.io.Writable;\nimport org.apache.hadoop.mapreduce.Job;\nimport org.apache.hadoop.mapreduce.Mapper;\nimport org.apache.hadoop.mapreduce.lib.input.FileInputFormat;\nimport org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;\nimport org.apache.hadoop.mapreduce.lib.reduce.LongSumReducer;\nimport org.apache.hadoop.util.GenericOptionsParser;\nimport org.apache.hadoop.util.Tool;\nimport org.apache.hadoop.util.ToolRunner;\n\nimport java.io.IOException;\nimport java.util.List;\nimport java.util.Map;\n\npublic class Wordcount extends Configured implements Tool {\n\n    // ----------------------------------------------------------------------\n\n    private final String logFormat;\n    public Wordcount(String logFormat) {\n        this.logFormat = logFormat;\n    }\n\n    // ----------------------------------------------------------------------\n\n    public static class TokenizerMapper extends\n            Mapper<Object, MapWritable, Text, LongWritable> {\n\n        private static final LongWritable ONE  = new LongWritable(1);\n        private final Text                word = new Text();\n\n        @Override\n        public void map(Object key, MapWritable value, Context context)\n            throws IOException, InterruptedException {\n            for (Map.Entry<Writable, Writable> entry : value.entrySet()) {\n                word.set(entry.getValue().toString());\n                context.write(word, ONE);\n            }\n        }\n    }\n\n    // ----------------------------------------------------------------------\n\n    @Override\n    public int run(String[] args) throws Exception {\n        Configuration conf = new Configuration();\n        String[] otherArgs = new GenericOptionsParser(conf, args)\n                .getRemainingArgs();\n        if (otherArgs.length != 2) {\n            System.err.println(\"Usage: wordcount <in> <out>\");\n            return 2;\n        }\n\n        conf.set(\"nl.basjes.parse.apachehttpdlogline.format\", logFormat);\n\n        // A ',' separated list of fields\n        conf.set(\"nl.basjes.parse.apachehttpdlogline.fields\",\n                \"STRING:request.status.last\");\n\n        Job job = Job.getInstance(conf, \"word count\");\n        job.setJarByClass(Wordcount.class);\n        FileInputFormat.addInputPath(job, new Path(otherArgs[0]));\n\n        job.setInputFormatClass(ApacheHttpdLogfileInputFormat.class);\n        job.setMapperClass(TokenizerMapper.class);\n        job.setCombinerClass(LongSumReducer.class);\n        job.setReducerClass(LongSumReducer.class);\n\n        // configuration should contain reference to your namenode\n        FileSystem fs = FileSystem.get(conf);\n        // true stands for recursively deleting the folder you gave\n        Path outputPath = new Path(otherArgs[1]);\n        fs.delete(outputPath, true);\n        FileOutputFormat.setOutputPath(job, outputPath);\n\n        job.setOutputKeyClass(Text.class);\n        job.setOutputValueClass(LongWritable.class);\n\n        if (job.waitForCompletion(true)) {\n            return 0;\n        }\n        return 1;\n    }\n\n    // ----------------------------------------------------------------------\n\n    public static void main(String[] args) throws Exception {\n\n        // httpd.conf has this next line:\n        //       LogFormat \"%h %l %u %t \\\"%r\\\" %>s %b \\\"%{Referer}i\\\" \\\"%{User-Agent}i\\\"\" combined\n        String logFormat = \"%h %l %u %t \\\"%r\\\" %>s %b \\\"%{Referer}i\\\" \\\"%{User-Agent}i\\\"\";\n\n        // Developer suggestion:\n        // This is what you do to find out what the possible fields are:\n        List<String> possibleFields = ApacheHttpdLogfileInputFormat\n                .listPossibleFields(logFormat, null, null);\n        System.out.println(\"----------------------------------------\");\n        System.out.println(\"All possible fields are:\");\n        for (String field : possibleFields) {\n            System.out.println(field);\n        }\n        System.out.println(\"----------------------------------------\");\n\n        System.exit(ToolRunner.run(new Configuration(), new Wordcount(logFormat), args));\n    }\n\n    // ----------------------------------------------------------------------\n\n}\n"
  },
  {
    "path": "examples/demolog/README.md",
    "content": "The file hackers-access.log is a sample of the access logs from my home webserver.\n\nThis file is in the 'combined' LogFormat\n\n    LogFormat \"%h %l %u %t \\\"%r\\\" %>s %b \\\"%{Referer}i\\\" \\\"%{User-Agent}i\\\"\" combined\n\nAlthough small (3456 lines) this is useful to do testing on parsing and aggregating the data.\n\nAll of these ips have accessed the /join_form URL.\nAlthough it exists this URL is nowhere advertised.\nSo only if you already 'know' this url should exist will a visitor try this.\nNo normal visitor knows this. Not even google knows this.\nSo the only people who 'know' this are hackers trying to break in (and fail in this case).\n\n    Apache HTTPD & NGINX Access log parsing made easy\n    Copyright (C) 2011-2023 Niels Basjes\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n    https://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n"
  },
  {
    "path": "examples/demolog/hackers-access.log",
    "content": "195.154.46.135 - - [25/Oct/2015:04:11:25 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.1\" 200 24323 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n23.95.237.180 - - [25/Oct/2015:04:11:26 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n23.95.237.180 - - [25/Oct/2015:04:11:27 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n158.222.5.157 - - [25/Oct/2015:04:24:31 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0 AlexaToolbar/alxf-2.21\"\n158.222.5.157 - - [25/Oct/2015:04:24:32 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0 AlexaToolbar/alxf-2.21\"\n158.222.5.157 - - [25/Oct/2015:04:24:37 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0 AlexaToolbar/alxf-2.21\"\n158.222.5.157 - - [25/Oct/2015:04:24:39 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0 AlexaToolbar/alxf-2.21\"\n158.222.5.157 - - [25/Oct/2015:04:24:41 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0 AlexaToolbar/alxf-2.21\"\n5.39.5.5 - - [25/Oct/2015:04:32:22 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:34.0) Gecko/20100101 Firefox/34.0\"\n180.180.64.16 - - [25/Oct/2015:04:34:37 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.1\" 200 24323 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n180.180.64.16 - - [25/Oct/2015:04:34:40 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n180.180.64.16 - - [25/Oct/2015:04:34:42 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n180.180.64.16 - - [25/Oct/2015:04:34:44 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n180.180.64.16 - - [25/Oct/2015:04:34:46 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n180.180.64.16 - - [25/Oct/2015:04:34:51 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n5.39.5.5 - - [25/Oct/2015:04:37:47 +0100] \"GET / HTTP/1.1\" 200 17057 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:34.0) Gecko/20100101 Firefox/34.0\"\n222.88.236.235 - - [25/Oct/2015:04:39:09 +0100] \"GET http://niels.basj.es/acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basj.es/join_form HTTP/1.0\" 200 11713 \"http://niels.basj.es/\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n222.88.236.235 - - [25/Oct/2015:04:39:12 +0100] \"GET http://niels.basj.es/acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basj.es/join_form HTTP/1.1\" 200 11713 \"http://niels.basj.es/\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n5.39.5.5 - - [25/Oct/2015:04:40:46 +0100] \"GET / HTTP/1.1\" 200 17057 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:34.0) Gecko/20100101 Firefox/34.0\"\n89.42.237.71 - - [25/Oct/2015:05:06:41 +0100] \"GET / HTTP/1.0\" 200 20478 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:34.0) Gecko/20100101 Firefox/34.0\"\n89.42.237.71 - - [25/Oct/2015:05:06:42 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:34.0) Gecko/20100101 Firefox/34.0\"\n89.42.237.71 - - [25/Oct/2015:05:06:43 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:34.0) Gecko/20100101 Firefox/34.0\"\n89.42.237.71 - - [25/Oct/2015:05:06:44 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:34.0) Gecko/20100101 Firefox/34.0\"\n89.42.237.71 - - [25/Oct/2015:05:06:45 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:34.0) Gecko/20100101 Firefox/34.0\"\n89.42.237.71 - - [25/Oct/2015:05:06:46 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:34.0) Gecko/20100101 Firefox/34.0\"\n200.55.25.2 - - [25/Oct/2015:05:07:22 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.0\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n200.55.25.2 - - [25/Oct/2015:05:07:26 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n200.55.25.2 - - [25/Oct/2015:05:07:32 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n211.196.252.10 - - [25/Oct/2015:05:09:12 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.0\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n211.196.252.10 - - [25/Oct/2015:05:09:14 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n211.196.252.10 - - [25/Oct/2015:05:09:17 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n192.227.222.207 - - [25/Oct/2015:05:22:22 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n192.227.222.207 - - [25/Oct/2015:05:22:23 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n46.102.99.22 - - [25/Oct/2015:05:32:22 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basj.es/join_form HTTP/1.0\" 200 11713 \"http://niels.basj.es/\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n46.102.99.22 - - [25/Oct/2015:05:32:23 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basj.es/join_form HTTP/1.1\" 200 11713 \"http://niels.basj.es/\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n216.158.199.158 - - [25/Oct/2015:05:40:09 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.0\" 200 24323 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.158.199.158 - - [25/Oct/2015:05:40:11 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.158.199.158 - - [25/Oct/2015:05:40:13 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.158.199.158 - - [25/Oct/2015:05:40:14 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:34.0) Gecko/20100101 Firefox/34.0\"\n158.222.12.158 - - [25/Oct/2015:05:44:33 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.0\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:34.0) Gecko/20100101 Firefox/34.0\"\n158.222.12.76 - - [25/Oct/2015:05:44:35 +0100] \"GET /login_form HTTP/1.0\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.158.199.158 - - [25/Oct/2015:05:44:36 +0100] \"POST /login_form HTTP/1.0\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:34.0) Gecko/20100101 Firefox/34.0\"\n167.160.127.164 - - [25/Oct/2015:05:57:27 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n167.160.127.164 - - [25/Oct/2015:05:57:28 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n167.160.127.164 - - [25/Oct/2015:05:57:29 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n167.160.127.164 - - [25/Oct/2015:05:57:30 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n167.160.127.164 - - [25/Oct/2015:05:57:30 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n167.160.127.164 - - [25/Oct/2015:05:57:31 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n192.126.177.54 - - [25/Oct/2015:06:01:29 +0100] \"GET /places HTTP/1.0\" 200 19359 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n192.126.177.54 - - [25/Oct/2015:06:01:31 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n192.126.177.54 - - [25/Oct/2015:06:01:33 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n192.126.177.54 - - [25/Oct/2015:06:01:34 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n104.128.23.51 - - [25/Oct/2015:06:04:07 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.0\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n104.128.23.51 - - [25/Oct/2015:06:04:09 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n204.44.85.215 - - [25/Oct/2015:06:18:21 +0100] \"GET / HTTP/1.1\" 200 20478 \"http://niels.basjes.nl\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0 AlexaToolbar/alxf-2.21\"\n204.44.85.215 - - [25/Oct/2015:06:18:23 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0 AlexaToolbar/alxf-2.21\"\n204.44.85.215 - - [25/Oct/2015:06:18:24 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0 AlexaToolbar/alxf-2.21\"\n204.44.85.215 - - [25/Oct/2015:06:18:26 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0 AlexaToolbar/alxf-2.21\"\n204.44.85.215 - - [25/Oct/2015:06:18:27 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0 AlexaToolbar/alxf-2.21\"\n204.44.85.215 - - [25/Oct/2015:06:18:28 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0 AlexaToolbar/alxf-2.21\"\n195.154.46.135 - - [25/Oct/2015:06:21:02 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n23.236.191.138 - - [25/Oct/2015:06:21:03 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.0\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n192.240.224.218 - - [25/Oct/2015:06:23:14 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.0\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:34.0) Gecko/20100101 Firefox/34.0\"\n192.240.224.218 - - [25/Oct/2015:06:23:15 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:34.0) Gecko/20100101 Firefox/34.0\"\n192.240.224.218 - - [25/Oct/2015:06:23:17 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:34.0) Gecko/20100101 Firefox/34.0\"\n201.234.239.28 - - [25/Oct/2015:06:51:18 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basj.es/join_form HTTP/1.1\" 200 11713 \"http://niels.basj.es/\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n201.234.239.28 - - [25/Oct/2015:06:51:22 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basj.es/join_form HTTP/1.1\" 200 11713 \"http://niels.basj.es/\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n91.196.122.132 - - [25/Oct/2015:07:42:22 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.0\" 200 24323 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n91.196.122.132 - - [25/Oct/2015:07:42:23 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n91.196.122.132 - - [25/Oct/2015:07:42:25 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n91.196.122.132 - - [25/Oct/2015:07:42:26 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n91.196.122.132 - - [25/Oct/2015:07:42:27 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n91.196.122.132 - - [25/Oct/2015:07:42:28 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n104.144.132.34 - - [25/Oct/2015:07:59:44 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36444 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n104.144.132.34 - - [25/Oct/2015:07:59:46 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n104.144.132.34 - - [25/Oct/2015:07:59:48 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n104.144.132.34 - - [25/Oct/2015:07:59:49 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n104.144.132.34 - - [25/Oct/2015:07:59:49 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n104.144.132.34 - - [25/Oct/2015:07:59:51 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n180.180.64.16 - - [25/Oct/2015:08:08:18 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.1\" 200 24323 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n180.180.64.16 - - [25/Oct/2015:08:08:21 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n180.180.64.16 - - [25/Oct/2015:08:08:22 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n180.180.64.16 - - [25/Oct/2015:08:08:24 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n180.180.64.16 - - [25/Oct/2015:08:08:26 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n180.180.64.16 - - [25/Oct/2015:08:08:28 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n1.0.189.181 - - [25/Oct/2015:08:13:00 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.1\" 200 24323 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n1.0.189.181 - - [25/Oct/2015:08:13:03 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n1.0.189.181 - - [25/Oct/2015:08:13:05 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n1.0.189.181 - - [25/Oct/2015:08:13:07 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n1.0.189.181 - - [25/Oct/2015:08:13:13 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n1.0.189.181 - - [25/Oct/2015:08:13:15 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n192.126.177.94 - - [25/Oct/2015:08:13:29 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.0\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n192.126.177.94 - - [25/Oct/2015:08:13:30 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n192.126.177.94 - - [25/Oct/2015:08:13:32 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n192.99.244.139 - - [25/Oct/2015:08:34:37 +0100] \"GET /accessibility-info HTTP/1.1\" 200 20626 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n192.99.244.139 - - [25/Oct/2015:08:34:38 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n192.99.244.139 - - [25/Oct/2015:08:34:39 +0100] \"POST /join_form HTTP/1.1\" 302 10322 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n192.99.244.139 - - [25/Oct/2015:08:34:39 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n192.99.244.139 - - [25/Oct/2015:08:34:40 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n192.99.244.139 - - [25/Oct/2015:08:34:41 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n1.0.189.181 - - [25/Oct/2015:08:35:53 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.1\" 200 24323 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n1.0.189.181 - - [25/Oct/2015:08:35:57 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n1.0.189.181 - - [25/Oct/2015:08:35:59 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n1.0.189.181 - - [25/Oct/2015:08:36:01 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n1.0.189.181 - - [25/Oct/2015:08:36:05 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n1.0.189.181 - - [25/Oct/2015:08:36:08 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n1.0.189.196 - - [25/Oct/2015:08:43:28 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.1\" 200 24323 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n1.0.189.196 - - [25/Oct/2015:08:43:31 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n1.0.189.196 - - [25/Oct/2015:08:43:33 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n1.0.189.196 - - [25/Oct/2015:08:43:35 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n1.0.189.196 - - [25/Oct/2015:08:43:40 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n1.0.189.196 - - [25/Oct/2015:08:43:42 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n89.36.65.53 - - [25/Oct/2015:08:49:40 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.0\" 200 24323 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n89.36.65.53 - - [25/Oct/2015:08:49:41 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n89.36.65.53 - - [25/Oct/2015:08:49:42 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n89.36.65.53 - - [25/Oct/2015:08:49:43 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n89.36.65.53 - - [25/Oct/2015:08:49:44 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n89.36.65.53 - - [25/Oct/2015:08:49:45 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n23.254.164.173 - - [25/Oct/2015:09:18:05 +0100] \"GET /linux/installing-my-new-server/voice-over-ip/RK=0/RS=YTkYTnGaf5QQxT7cm9uJgKS2rl8- HTTP/1.1\" 404 12146 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.112 Safari/535.1\"\n23.254.164.173 - - [25/Oct/2015:09:18:06 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/linux/installing-my-new-server/voice-over-ip/RK=0/RS=YTkYTnGaf5QQxT7cm9uJgKS2rl8-\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.112 Safari/535.1\"\n23.254.164.173 - - [25/Oct/2015:09:18:07 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.112 Safari/535.1\"\n23.254.164.173 - - [25/Oct/2015:09:18:08 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.112 Safari/535.1\"\n23.254.164.173 - - [25/Oct/2015:09:18:11 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.112 Safari/535.1\"\n23.254.164.173 - - [25/Oct/2015:09:18:11 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.112 Safari/535.1\"\n223.92.118.91 - - [25/Oct/2015:09:23:16 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.0\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n115.236.7.180 - - [25/Oct/2015:09:23:50 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.0\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n115.236.7.180 - - [25/Oct/2015:09:23:54 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.0\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n31.220.30.157 - - [25/Oct/2015:09:30:33 +0100] \"GET / HTTP/1.0\" 200 20478 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n31.220.30.157 - - [25/Oct/2015:09:30:35 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n31.220.30.157 - - [25/Oct/2015:09:30:36 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n195.154.46.135 - - [25/Oct/2015:09:33:36 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n23.95.100.64 - - [25/Oct/2015:09:33:37 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n23.95.100.64 - - [25/Oct/2015:09:33:39 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n23.95.100.64 - - [25/Oct/2015:09:33:40 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n112.179.44.151 - - [25/Oct/2015:09:55:33 +0100] \"GET /places HTTP/1.1\" 200 19359 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n112.179.44.151 - - [25/Oct/2015:09:55:36 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n112.179.44.151 - - [25/Oct/2015:09:55:38 +0100] \"POST /join_form HTTP/1.1\" 302 10322 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n112.179.44.151 - - [25/Oct/2015:09:55:40 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n112.179.44.151 - - [25/Oct/2015:09:55:42 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n112.179.44.151 - - [25/Oct/2015:09:55:44 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n192.240.224.229 - - [25/Oct/2015:10:10:09 +0100] \"GET / HTTP/1.0\" 200 20478 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n192.240.224.229 - - [25/Oct/2015:10:10:10 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n192.240.224.229 - - [25/Oct/2015:10:10:11 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n192.240.224.229 - - [25/Oct/2015:10:10:12 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n180.180.104.111 - - [25/Oct/2015:10:33:02 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.1\" 200 24371 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n180.180.104.111 - - [25/Oct/2015:10:33:04 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n180.180.104.111 - - [25/Oct/2015:10:33:06 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n180.180.104.111 - - [25/Oct/2015:10:33:07 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n180.180.104.111 - - [25/Oct/2015:10:33:17 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n180.180.104.111 - - [25/Oct/2015:10:33:19 +0100] \"POST /login_form HTTP/1.1\" 200 16858 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n5.157.42.183 - - [25/Oct/2015:10:47:37 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.0\" 200 24371 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n5.157.42.183 - - [25/Oct/2015:10:47:38 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n5.157.42.183 - - [25/Oct/2015:10:47:39 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n5.157.42.183 - - [25/Oct/2015:10:47:40 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n5.157.42.183 - - [25/Oct/2015:10:47:41 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n5.157.42.183 - - [25/Oct/2015:10:47:42 +0100] \"POST /login_form HTTP/1.1\" 200 16858 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n204.44.85.38 - - [25/Oct/2015:10:51:36 +0100] \"GET / HTTP/1.1\" 200 20478 \"http://niels.basjes.nl\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n204.44.85.38 - - [25/Oct/2015:10:51:37 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n204.44.85.38 - - [25/Oct/2015:10:51:39 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n204.44.85.38 - - [25/Oct/2015:10:51:40 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n204.44.85.38 - - [25/Oct/2015:10:51:41 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n204.44.85.38 - - [25/Oct/2015:10:51:43 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n103.27.239.39 - - [25/Oct/2015:10:52:44 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form&last_visit:date=2015%2F10%2F13+18%3A31%3A02.492+GMT%2B2&prev_visit:date=2015%2F10%2F13+18%3A31%3A02.493+GMT%2B2&came_from_prefs=&fullname=Eileen+Coe&username=EileenCoe&email=t.h.u.c.d.v2016%40gmail.com&form.button.Register=Register&form.submitted=1 HTTP/1.1\" 200 19089 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n103.27.239.39 - - [25/Oct/2015:10:52:46 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n103.27.239.39 - - [25/Oct/2015:10:52:48 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n103.27.239.39 - - [25/Oct/2015:10:52:50 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n103.27.239.39 - - [25/Oct/2015:10:52:51 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n103.27.239.39 - - [25/Oct/2015:10:52:54 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n178.216.54.163 - - [25/Oct/2015:11:00:49 +0100] \"GET /join_form HTTP/1.0\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n178.216.54.163 - - [25/Oct/2015:11:00:51 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n178.216.54.163 - - [25/Oct/2015:11:00:52 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n178.216.54.163 - - [25/Oct/2015:11:00:53 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n178.216.54.163 - - [25/Oct/2015:11:00:55 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n107.172.9.148 - - [25/Oct/2015:11:08:47 +0100] \"GET /places HTTP/1.0\" 200 19359 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n107.172.9.148 - - [25/Oct/2015:11:08:49 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n107.172.9.148 - - [25/Oct/2015:11:08:50 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n107.172.9.148 - - [25/Oct/2015:11:08:51 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n107.172.9.148 - - [25/Oct/2015:11:08:52 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n107.172.9.148 - - [25/Oct/2015:11:08:54 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n104.223.44.139 - - [25/Oct/2015:11:17:09 +0100] \"GET /linux/installing-my-new-server/vmware-server HTTP/1.1\" 200 25009 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n104.223.44.139 - - [25/Oct/2015:11:17:11 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n104.223.44.139 - - [25/Oct/2015:11:17:12 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n104.223.44.139 - - [25/Oct/2015:11:17:13 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n104.223.44.139 - - [25/Oct/2015:11:17:14 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n104.223.44.139 - - [25/Oct/2015:11:17:15 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n91.108.71.198 - - [25/Oct/2015:11:32:51 +0100] \"GET /join_form HTTP/1.0\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n91.108.71.198 - - [25/Oct/2015:11:32:52 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n91.108.71.198 - - [25/Oct/2015:11:32:52 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n91.108.71.198 - - [25/Oct/2015:11:32:53 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n91.108.71.198 - - [25/Oct/2015:11:32:53 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n107.155.136.75 - - [25/Oct/2015:11:35:05 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.0\" 200 24323 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n107.155.136.75 - - [25/Oct/2015:11:35:07 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n107.155.136.75 - - [25/Oct/2015:11:35:08 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n107.155.136.75 - - [25/Oct/2015:11:35:09 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n107.155.136.75 - - [25/Oct/2015:11:35:11 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n107.155.136.75 - - [25/Oct/2015:11:35:12 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n1.0.190.64 - - [25/Oct/2015:12:18:11 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.1\" 200 24323 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n1.0.190.64 - - [25/Oct/2015:12:18:17 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n1.0.190.64 - - [25/Oct/2015:12:18:19 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n1.0.190.64 - - [25/Oct/2015:12:18:23 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n1.0.190.64 - - [25/Oct/2015:12:18:25 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n1.0.190.64 - - [25/Oct/2015:12:18:26 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n104.247.99.226 - - [25/Oct/2015:12:40:28 +0100] \"GET / HTTP/1.0\" 200 20478 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n104.247.99.226 - - [25/Oct/2015:12:40:31 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n104.247.99.226 - - [25/Oct/2015:12:40:33 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n104.247.99.226 - - [25/Oct/2015:12:40:35 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n104.247.99.226 - - [25/Oct/2015:12:40:36 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n104.247.99.226 - - [25/Oct/2015:12:40:38 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n172.245.229.166 - - [25/Oct/2015:12:45:13 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n172.245.229.166 - - [25/Oct/2015:12:45:14 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n190.60.108.2 - - [25/Oct/2015:12:52:27 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n190.60.108.2 - - [25/Oct/2015:12:52:28 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n190.60.108.2 - - [25/Oct/2015:12:52:30 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n190.60.108.2 - - [25/Oct/2015:12:52:33 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n190.60.108.2 - - [25/Oct/2015:12:52:35 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n104.223.45.5 - - [25/Oct/2015:13:10:33 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n192.161.190.121 - - [25/Oct/2015:13:10:35 +0100] \"POST /join_form HTTP/1.0\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n192.161.190.121 - - [25/Oct/2015:13:10:36 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.0\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n192.161.190.121 - - [25/Oct/2015:13:10:38 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n192.161.190.121 - - [25/Oct/2015:13:10:39 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n155.254.119.252 - - [25/Oct/2015:13:11:40 +0100] \"GET /join_form HTTP/1.0\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n155.254.119.252 - - [25/Oct/2015:13:11:41 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n155.254.119.252 - - [25/Oct/2015:13:11:42 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n155.254.119.252 - - [25/Oct/2015:13:11:43 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n155.254.119.252 - - [25/Oct/2015:13:11:44 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n31.220.113.224 - - [25/Oct/2015:13:13:59 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.0\" 200 24323 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n31.220.113.224 - - [25/Oct/2015:13:14:00 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n31.220.113.224 - - [25/Oct/2015:13:14:00 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n31.220.113.224 - - [25/Oct/2015:13:14:02 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n31.220.113.224 - - [25/Oct/2015:13:14:02 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n31.220.113.224 - - [25/Oct/2015:13:14:02 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n1.0.189.90 - - [25/Oct/2015:13:16:49 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.1\" 200 24323 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n1.0.189.90 - - [25/Oct/2015:13:16:52 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n1.0.189.90 - - [25/Oct/2015:13:16:54 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n1.0.189.90 - - [25/Oct/2015:13:16:56 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n1.0.189.90 - - [25/Oct/2015:13:16:58 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n1.0.189.90 - - [25/Oct/2015:13:17:00 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n115.231.162.216 - - [25/Oct/2015:13:18:29 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//daniel_en_sander.basjes.nl/join_form HTTP/1.1\" 403 340 \"http://daniel_en_sander.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n115.231.162.216 - - [25/Oct/2015:13:18:32 +0100] \"GET / HTTP/1.1\" 200 15381 \"http://daniel_en_sander.basjes.nl\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n195.154.46.135 - - [25/Oct/2015:13:19:17 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n104.223.29.240 - - [25/Oct/2015:13:19:18 +0100] \"GET /login_form HTTP/1.0\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n104.223.29.240 - - [25/Oct/2015:13:19:20 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n23.254.164.173 - - [25/Oct/2015:13:20:50 +0100] \"GET /linux/installing-my-new-server/Various-software/RK=0/RS=C_oF6PJOAKvJ3JsgiwAbhNKI2uE- HTTP/1.1\" 404 12145 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.112 Safari/535.1\"\n23.254.164.173 - - [25/Oct/2015:13:20:51 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/linux/installing-my-new-server/Various-software/RK=0/RS=C_oF6PJOAKvJ3JsgiwAbhNKI2uE-\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.112 Safari/535.1\"\n23.254.164.173 - - [25/Oct/2015:13:20:52 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.112 Safari/535.1\"\n23.254.164.173 - - [25/Oct/2015:13:20:54 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.112 Safari/535.1\"\n23.254.164.173 - - [25/Oct/2015:13:20:56 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.112 Safari/535.1\"\n23.254.164.173 - - [25/Oct/2015:13:20:56 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.112 Safari/535.1\"\n104.140.71.73 - - [25/Oct/2015:13:35:57 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36444 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n104.140.71.73 - - [25/Oct/2015:13:35:59 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n104.140.71.73 - - [25/Oct/2015:13:36:00 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n104.140.71.73 - - [25/Oct/2015:13:36:01 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n104.140.71.73 - - [25/Oct/2015:13:36:02 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n104.140.71.73 - - [25/Oct/2015:13:36:02 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n23.247.182.39 - - [25/Oct/2015:13:52:24 +0100] \"GET / HTTP/1.0\" 200 20478 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n23.247.182.39 - - [25/Oct/2015:13:52:25 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n23.247.182.39 - - [25/Oct/2015:13:52:26 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n23.247.182.39 - - [25/Oct/2015:13:52:27 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n23.247.182.39 - - [25/Oct/2015:13:52:28 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n23.247.182.39 - - [25/Oct/2015:13:52:29 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n117.169.1.51 - - [25/Oct/2015:13:57:38 +0100] \"GET /places HTTP/1.1\" 200 19359 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n117.169.1.51 - - [25/Oct/2015:13:57:47 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n117.169.1.51 - - [25/Oct/2015:13:57:55 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n117.169.1.51 - - [25/Oct/2015:13:57:57 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n113.215.0.130 - - [25/Oct/2015:14:25:42 +0100] \"GET http://howto.basjes.nl/join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n113.215.0.130 - - [25/Oct/2015:14:25:45 +0100] \"POST http://howto.basjes.nl/join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n113.215.0.130 - - [25/Oct/2015:14:25:51 +0100] \"GET http://howto.basjes.nl/acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n113.215.0.130 - - [25/Oct/2015:14:25:55 +0100] \"GET http://howto.basjes.nl/login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n113.215.0.130 - - [25/Oct/2015:14:25:58 +0100] \"POST http://howto.basjes.nl/login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n167.160.127.104 - - [25/Oct/2015:15:03:33 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n167.160.127.104 - - [25/Oct/2015:15:03:35 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n104.144.132.106 - - [25/Oct/2015:15:08:23 +0100] \"GET / HTTP/1.1\" 200 20478 \"http://niels.basjes.nl\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n104.144.132.106 - - [25/Oct/2015:15:08:25 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n104.144.132.106 - - [25/Oct/2015:15:08:25 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n104.144.132.106 - - [25/Oct/2015:15:08:27 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n104.144.132.106 - - [25/Oct/2015:15:08:27 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n104.144.132.106 - - [25/Oct/2015:15:08:28 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n216.158.196.63 - - [25/Oct/2015:16:02:58 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.0\" 200 24323 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n216.158.196.63 - - [25/Oct/2015:16:03:00 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n216.158.196.63 - - [25/Oct/2015:16:03:01 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n216.158.196.63 - - [25/Oct/2015:16:03:02 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n216.158.200.60 - - [25/Oct/2015:16:03:33 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.0\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n216.158.200.60 - - [25/Oct/2015:16:03:35 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n216.158.200.60 - - [25/Oct/2015:16:03:36 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n192.161.163.180 - - [25/Oct/2015:16:35:06 +0100] \"GET / HTTP/1.0\" 200 20478 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n192.161.163.180 - - [25/Oct/2015:16:35:08 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n192.161.163.180 - - [25/Oct/2015:16:35:10 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n192.210.223.81 - - [25/Oct/2015:16:46:54 +0100] \"GET / HTTP/1.1\" 200 20478 \"http://niels.basjes.nl\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n192.210.223.81 - - [25/Oct/2015:16:46:55 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n192.210.223.81 - - [25/Oct/2015:16:46:56 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n192.210.223.81 - - [25/Oct/2015:16:46:57 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n192.210.223.81 - - [25/Oct/2015:16:46:58 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n192.210.223.81 - - [25/Oct/2015:16:46:59 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n180.180.106.127 - - [25/Oct/2015:17:01:29 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.1\" 200 24323 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n180.180.106.127 - - [25/Oct/2015:17:01:33 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n180.180.106.127 - - [25/Oct/2015:17:01:36 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n180.180.106.127 - - [25/Oct/2015:17:01:38 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n180.180.106.127 - - [25/Oct/2015:17:01:40 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n180.180.106.127 - - [25/Oct/2015:17:01:42 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n198.46.242.48 - - [25/Oct/2015:17:06:19 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n198.46.242.48 - - [25/Oct/2015:17:06:20 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n198.46.242.48 - - [25/Oct/2015:17:06:24 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n198.46.242.48 - - [25/Oct/2015:17:06:27 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n198.46.242.48 - - [25/Oct/2015:17:06:28 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n23.95.60.151 - - [25/Oct/2015:17:11:46 +0100] \"GET / HTTP/1.0\" 200 20478 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n23.95.60.151 - - [25/Oct/2015:17:11:48 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n23.95.60.151 - - [25/Oct/2015:17:11:52 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n23.95.60.151 - - [25/Oct/2015:17:12:01 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n23.95.60.151 - - [25/Oct/2015:17:12:17 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n23.95.60.151 - - [25/Oct/2015:17:12:19 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n62.210.141.226 - - [25/Oct/2015:17:23:39 +0100] \"GET /linux/installing-my-new-server/networking HTTP/1.1\" 200 27452 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n192.3.190.14 - - [25/Oct/2015:17:23:40 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"-\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n192.3.190.14 - - [25/Oct/2015:17:23:41 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"-\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n192.3.190.14 - - [25/Oct/2015:17:23:42 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"-\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n62.210.141.226 - - [25/Oct/2015:17:29:27 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n69.12.66.210 - - [25/Oct/2015:17:29:28 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.0\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n46.102.99.131 - - [25/Oct/2015:17:37:07 +0100] \"GET /sitemap HTTP/1.0\" 200 11975 \"http://niels.basj.es/\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n46.102.99.131 - - [25/Oct/2015:17:37:08 +0100] \"GET /join_form HTTP/1.1\" 200 12121 \"http://niels.basj.es/\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n46.102.99.131 - - [25/Oct/2015:17:37:09 +0100] \"POST /join_form HTTP/1.1\" 302 10098 \"http://niels.basj.es/join_form\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n46.102.99.131 - - [25/Oct/2015:17:37:11 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basj.es/join_form HTTP/1.1\" 200 11713 \"http://niels.basj.es/join_form\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n46.102.99.131 - - [25/Oct/2015:17:37:12 +0100] \"GET /login_form HTTP/1.1\" 200 11544 \"http://niels.basj.es/\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n46.102.99.131 - - [25/Oct/2015:17:37:14 +0100] \"POST /login_form HTTP/1.1\" 200 18241 \"http://niels.basj.es/login_form\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n5.175.219.94 - - [25/Oct/2015:17:53:41 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.0\" 200 24323 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n5.175.219.94 - - [25/Oct/2015:17:53:45 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n5.175.219.94 - - [25/Oct/2015:17:53:46 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n5.175.219.94 - - [25/Oct/2015:17:53:48 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n5.175.219.94 - - [25/Oct/2015:17:53:49 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n5.175.219.94 - - [25/Oct/2015:17:53:51 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n110.208.27.102 - - [25/Oct/2015:17:56:59 +0100] \"GET /places HTTP/1.1\" 200 19359 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n110.208.27.102 - - [25/Oct/2015:17:57:01 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n110.208.27.102 - - [25/Oct/2015:17:57:02 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n110.208.27.102 - - [25/Oct/2015:17:57:04 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n110.208.27.102 - - [25/Oct/2015:17:57:05 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n110.208.27.102 - - [25/Oct/2015:17:57:08 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n104.254.212.109 - - [25/Oct/2015:18:01:09 +0100] \"GET /splittable-gzip HTTP/1.1\" 200 27873 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n104.254.212.109 - - [25/Oct/2015:18:01:10 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n104.254.212.109 - - [25/Oct/2015:18:01:11 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n104.254.212.109 - - [25/Oct/2015:18:01:12 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n104.254.212.109 - - [25/Oct/2015:18:01:13 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n104.254.212.109 - - [25/Oct/2015:18:01:14 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n62.210.141.226 - - [25/Oct/2015:18:08:34 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n192.99.244.139 - - [25/Oct/2015:18:22:13 +0100] \"GET /accessibility-info HTTP/1.1\" 200 20626 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n192.99.244.139 - - [25/Oct/2015:18:22:13 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n192.99.244.139 - - [25/Oct/2015:18:22:14 +0100] \"POST /join_form HTTP/1.1\" 302 10322 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n192.99.244.139 - - [25/Oct/2015:18:22:15 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n192.99.244.139 - - [25/Oct/2015:18:22:16 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n192.99.244.139 - - [25/Oct/2015:18:22:17 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n94.249.245.51 - - [25/Oct/2015:18:44:42 +0100] \"GET /linux/installing-my-new-server/networking HTTP/1.0\" 200 27452 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n94.249.245.51 - - [25/Oct/2015:18:44:43 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n94.249.245.51 - - [25/Oct/2015:18:44:44 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n94.249.245.51 - - [25/Oct/2015:18:44:45 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n94.249.245.51 - - [25/Oct/2015:18:44:46 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n94.249.245.51 - - [25/Oct/2015:18:44:47 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n180.180.97.153 - - [25/Oct/2015:18:46:38 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.1\" 200 24323 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n180.180.97.153 - - [25/Oct/2015:18:46:40 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n180.180.97.153 - - [25/Oct/2015:18:46:43 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n180.180.97.153 - - [25/Oct/2015:18:46:48 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n180.180.97.153 - - [25/Oct/2015:18:46:49 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n180.180.97.153 - - [25/Oct/2015:18:46:54 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.158.222.163 - - [25/Oct/2015:18:52:10 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.0\" 200 24323 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n216.158.222.163 - - [25/Oct/2015:18:52:12 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n216.158.222.163 - - [25/Oct/2015:18:52:14 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n216.158.222.163 - - [25/Oct/2015:18:52:15 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n158.222.5.244 - - [25/Oct/2015:18:52:46 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.0\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n192.200.24.243 - - [25/Oct/2015:19:31:51 +0100] \"GET / HTTP/1.0\" 200 20526 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n192.200.24.243 - - [25/Oct/2015:19:31:52 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n192.200.24.243 - - [25/Oct/2015:19:31:54 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n192.200.24.243 - - [25/Oct/2015:19:31:55 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n192.200.24.243 - - [25/Oct/2015:19:31:57 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n192.200.24.243 - - [25/Oct/2015:19:31:58 +0100] \"POST /login_form HTTP/1.1\" 200 18402 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n172.245.224.141 - - [25/Oct/2015:19:31:59 +0100] \"GET / HTTP/1.1\" 200 20526 \"http://niels.basjes.nl\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n172.245.224.141 - - [25/Oct/2015:19:32:00 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n172.245.224.141 - - [25/Oct/2015:19:32:02 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n172.245.224.141 - - [25/Oct/2015:19:32:03 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n172.245.224.141 - - [25/Oct/2015:19:32:04 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n172.245.224.141 - - [25/Oct/2015:19:32:05 +0100] \"POST /login_form HTTP/1.1\" 200 18402 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n186.206.255.185 - - [25/Oct/2015:19:49:37 +0100] \"GET /linux/installing-my-new-server/networking HTTP/1.0\" 200 27452 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n186.206.255.185 - - [25/Oct/2015:19:49:57 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n186.206.255.185 - - [25/Oct/2015:19:50:02 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n186.206.255.185 - - [25/Oct/2015:19:50:29 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n186.206.255.185 - - [25/Oct/2015:19:50:43 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n186.206.255.185 - - [25/Oct/2015:19:50:51 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n45.33.146.75 - - [25/Oct/2015:20:06:03 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36444 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n45.33.146.75 - - [25/Oct/2015:20:06:06 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n45.33.146.75 - - [25/Oct/2015:20:06:07 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n45.33.146.75 - - [25/Oct/2015:20:06:11 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n45.33.146.75 - - [25/Oct/2015:20:06:12 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n45.33.146.75 - - [25/Oct/2015:20:06:13 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n107.172.0.248 - - [25/Oct/2015:20:08:30 +0100] \"GET /places HTTP/1.0\" 200 19407 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n107.172.0.248 - - [25/Oct/2015:20:08:32 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n107.172.0.248 - - [25/Oct/2015:20:08:33 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n107.172.0.248 - - [25/Oct/2015:20:08:34 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n107.172.0.248 - - [25/Oct/2015:20:08:35 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n107.172.0.248 - - [25/Oct/2015:20:08:36 +0100] \"POST /login_form HTTP/1.1\" 200 18402 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n5.175.177.240 - - [25/Oct/2015:20:27:45 +0100] \"GET /author/nielsbasjes HTTP/1.0\" 200 15519 \"http://niels.basj.es/\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n46.102.99.141 - - [25/Oct/2015:20:27:52 +0100] \"GET /join_form HTTP/1.0\" 200 12121 \"http://niels.basj.es/\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n46.102.99.141 - - [25/Oct/2015:20:27:53 +0100] \"POST /join_form HTTP/1.1\" 302 10098 \"http://niels.basj.es/join_form\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n46.102.99.141 - - [25/Oct/2015:20:27:53 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basj.es/join_form HTTP/1.1\" 200 11713 \"http://niels.basj.es/join_form\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n46.102.99.141 - - [25/Oct/2015:20:27:54 +0100] \"GET /login_form HTTP/1.1\" 200 11544 \"http://niels.basj.es/\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n46.102.99.141 - - [25/Oct/2015:20:27:55 +0100] \"POST /login_form HTTP/1.1\" 200 18241 \"http://niels.basj.es/login_form\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n62.210.204.180 - - [25/Oct/2015:21:18:05 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n62.210.204.180 - - [25/Oct/2015:21:18:05 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n104.140.71.108 - - [25/Oct/2015:21:46:44 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36444 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n104.140.71.108 - - [25/Oct/2015:21:46:46 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n104.140.71.108 - - [25/Oct/2015:21:46:47 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n104.140.71.108 - - [25/Oct/2015:21:46:47 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n104.140.71.108 - - [25/Oct/2015:21:46:48 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n104.140.71.108 - - [25/Oct/2015:21:46:50 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n62.210.204.180 - - [25/Oct/2015:22:13:30 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n62.210.204.180 - - [25/Oct/2015:22:13:30 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n46.102.99.140 - - [25/Oct/2015:22:25:34 +0100] \"GET /sitemap HTTP/1.0\" 200 11975 \"http://niels.basj.es/\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n46.102.99.140 - - [25/Oct/2015:22:25:35 +0100] \"GET /join_form HTTP/1.1\" 200 12121 \"http://niels.basj.es/\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n46.102.99.140 - - [25/Oct/2015:22:25:36 +0100] \"POST /join_form HTTP/1.1\" 302 10098 \"http://niels.basj.es/join_form\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n46.102.99.140 - - [25/Oct/2015:22:25:37 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basj.es/join_form HTTP/1.1\" 200 11713 \"http://niels.basj.es/join_form\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n46.102.99.140 - - [25/Oct/2015:22:25:41 +0100] \"GET /login_form HTTP/1.1\" 200 11544 \"http://niels.basj.es/\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n46.102.99.140 - - [25/Oct/2015:22:25:44 +0100] \"POST /login_form HTTP/1.1\" 200 18241 \"http://niels.basj.es/login_form\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n91.99.99.59 - - [25/Oct/2015:22:45:30 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.1\" 200 24323 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n91.99.99.59 - - [25/Oct/2015:22:45:32 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n125.140.118.12 - - [25/Oct/2015:22:45:41 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n125.140.118.12 - - [25/Oct/2015:22:45:43 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n125.140.118.12 - - [25/Oct/2015:22:45:47 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n125.140.118.12 - - [25/Oct/2015:22:45:50 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n81.83.39.195 - - [25/Oct/2015:23:01:05 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n81.83.39.195 - - [25/Oct/2015:23:01:07 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n81.83.39.195 - - [25/Oct/2015:23:01:17 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n81.83.39.195 - - [25/Oct/2015:23:01:18 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n170.130.66.205 - - [25/Oct/2015:23:25:12 +0100] \"GET /linux/installing-my-new-server/networking HTTP/1.0\" 200 27452 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n170.130.66.205 - - [25/Oct/2015:23:25:24 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n170.130.66.205 - - [25/Oct/2015:23:25:28 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n170.130.66.205 - - [25/Oct/2015:23:25:35 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n170.130.66.205 - - [25/Oct/2015:23:25:39 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n170.130.66.205 - - [25/Oct/2015:23:25:46 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n158.222.12.39 - - [25/Oct/2015:23:40:46 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36444 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n158.222.12.39 - - [25/Oct/2015:23:40:48 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n158.222.12.39 - - [25/Oct/2015:23:40:49 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n158.222.12.39 - - [25/Oct/2015:23:40:51 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n158.222.12.39 - - [25/Oct/2015:23:40:52 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n158.222.12.39 - - [25/Oct/2015:23:40:53 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n64.110.135.40 - - [26/Oct/2015:00:00:02 +0100] \"GET /places HTTP/1.0\" 200 19355 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n64.110.135.40 - - [26/Oct/2015:00:00:04 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n64.110.135.40 - - [26/Oct/2015:00:00:05 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n64.110.135.40 - - [26/Oct/2015:00:00:06 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n64.110.135.40 - - [26/Oct/2015:00:00:07 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n64.110.135.40 - - [26/Oct/2015:00:00:08 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n121.204.74.138 - - [26/Oct/2015:00:13:17 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36440 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.99 Safari/537.36\"\n192.210.223.92 - - [26/Oct/2015:00:14:15 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36440 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n192.210.223.92 - - [26/Oct/2015:00:14:18 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n192.210.223.92 - - [26/Oct/2015:00:14:18 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n192.210.223.92 - - [26/Oct/2015:00:14:19 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n192.210.223.92 - - [26/Oct/2015:00:14:20 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n192.210.223.92 - - [26/Oct/2015:00:14:21 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n23.95.91.209 - - [26/Oct/2015:00:27:38 +0100] \"GET /linux/installing-my-new-server/networking HTTP/1.0\" 200 27448 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n23.95.91.209 - - [26/Oct/2015:00:27:39 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n23.95.91.209 - - [26/Oct/2015:00:27:40 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n23.95.91.209 - - [26/Oct/2015:00:27:41 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n23.95.91.209 - - [26/Oct/2015:00:27:42 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n23.95.91.209 - - [26/Oct/2015:00:27:43 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n190.136.18.6 - - [26/Oct/2015:00:31:03 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.1\" 200 24319 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n190.136.18.6 - - [26/Oct/2015:00:31:09 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n190.136.18.6 - - [26/Oct/2015:00:31:13 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n190.136.18.6 - - [26/Oct/2015:00:31:16 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n190.136.18.6 - - [26/Oct/2015:00:31:26 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n190.136.18.6 - - [26/Oct/2015:00:31:30 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n50.7.8.74 - - [26/Oct/2015:00:44:20 +0100] \"GET / HTTP/1.0\" 200 15377 \"http://daniel_en_sander.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n50.7.8.74 - - [26/Oct/2015:00:44:25 +0100] \"GET /join_form HTTP/1.1\" 200 11652 \"http://daniel_en_sander.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n50.7.8.74 - - [26/Oct/2015:00:44:34 +0100] \"POST /join_form HTTP/1.1\" 302 9556 \"http://daniel_en_sander.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n50.7.8.74 - - [26/Oct/2015:00:44:47 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//daniel_en_sander.basjes.nl/join_form HTTP/1.1\" 403 340 \"http://daniel_en_sander.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n180.180.103.37 - - [26/Oct/2015:00:46:18 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.1\" 200 24319 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n180.180.103.37 - - [26/Oct/2015:00:46:20 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n180.180.103.37 - - [26/Oct/2015:00:46:22 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n180.180.103.37 - - [26/Oct/2015:00:46:23 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n180.180.103.37 - - [26/Oct/2015:00:46:25 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n23.231.24.17 - - [26/Oct/2015:00:46:31 +0100] \"GET / HTTP/1.1\" 200 20474 \"http://niels.basjes.nl\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n23.231.24.17 - - [26/Oct/2015:00:46:33 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n180.180.103.37 - - [26/Oct/2015:00:46:33 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n23.231.24.17 - - [26/Oct/2015:00:46:34 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n23.231.24.17 - - [26/Oct/2015:00:46:35 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n23.231.24.17 - - [26/Oct/2015:00:46:36 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n23.231.24.17 - - [26/Oct/2015:00:46:38 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n104.144.132.106 - - [26/Oct/2015:01:24:10 +0100] \"GET / HTTP/1.1\" 200 20474 \"http://niels.basjes.nl\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n104.144.132.106 - - [26/Oct/2015:01:24:12 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n104.144.132.106 - - [26/Oct/2015:01:24:13 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n104.144.132.106 - - [26/Oct/2015:01:24:14 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n104.144.132.106 - - [26/Oct/2015:01:24:15 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n104.144.132.106 - - [26/Oct/2015:01:24:16 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n180.180.102.54 - - [26/Oct/2015:01:38:38 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.1\" 200 24319 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n180.180.102.54 - - [26/Oct/2015:01:38:40 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n180.180.102.54 - - [26/Oct/2015:01:38:42 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n180.180.102.54 - - [26/Oct/2015:01:38:43 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n180.180.102.54 - - [26/Oct/2015:01:38:46 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n180.180.102.54 - - [26/Oct/2015:01:38:49 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n198.52.212.158 - - [26/Oct/2015:01:52:59 +0100] \"GET /linux/installing-my-new-server/networking HTTP/1.0\" 200 27448 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n198.52.212.158 - - [26/Oct/2015:01:53:00 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n198.52.212.158 - - [26/Oct/2015:01:53:01 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n198.52.212.158 - - [26/Oct/2015:01:53:02 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n198.52.212.158 - - [26/Oct/2015:01:53:04 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n198.52.212.158 - - [26/Oct/2015:01:53:05 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n211.103.148.66 - - [26/Oct/2015:01:57:46 +0100] \"GET / HTTP/1.0\" 200 20474 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n211.103.148.66 - - [26/Oct/2015:01:57:50 +0100] \"GET /join_form HTTP/1.0\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n211.103.148.66 - - [26/Oct/2015:01:57:52 +0100] \"POST /join_form HTTP/1.0\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n107.158.100.171 - - [26/Oct/2015:02:29:18 +0100] \"GET / HTTP/1.0\" 200 15377 \"http://daniel_en_sander.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n107.158.100.171 - - [26/Oct/2015:02:29:19 +0100] \"GET /join_form HTTP/1.1\" 200 11652 \"http://daniel_en_sander.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n107.158.100.171 - - [26/Oct/2015:02:29:21 +0100] \"POST /join_form HTTP/1.1\" 302 9556 \"http://daniel_en_sander.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n107.158.100.171 - - [26/Oct/2015:02:29:22 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//daniel_en_sander.basjes.nl/join_form HTTP/1.1\" 403 340 \"http://daniel_en_sander.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n196.196.34.116 - - [26/Oct/2015:02:30:10 +0100] \"GET / HTTP/1.0\" 200 17053 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n196.196.34.116 - - [26/Oct/2015:02:30:12 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n196.196.34.116 - - [26/Oct/2015:02:30:13 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n196.196.34.116 - - [26/Oct/2015:02:30:13 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n196.196.34.116 - - [26/Oct/2015:02:30:14 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n196.196.34.116 - - [26/Oct/2015:02:30:14 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n180.180.73.221 - - [26/Oct/2015:02:33:03 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.1\" 200 24319 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n180.180.73.221 - - [26/Oct/2015:02:33:05 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n180.180.73.221 - - [26/Oct/2015:02:33:07 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n180.180.73.221 - - [26/Oct/2015:02:33:09 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n180.180.73.221 - - [26/Oct/2015:02:33:11 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n180.180.73.221 - - [26/Oct/2015:02:33:12 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n1.0.176.33 - - [26/Oct/2015:02:41:26 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.1\" 200 24319 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n1.0.176.33 - - [26/Oct/2015:02:41:29 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n1.0.176.33 - - [26/Oct/2015:02:41:31 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n1.0.176.33 - - [26/Oct/2015:02:41:32 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n1.0.176.33 - - [26/Oct/2015:02:41:34 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n1.0.176.33 - - [26/Oct/2015:02:41:36 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n107.158.89.247 - - [26/Oct/2015:02:53:59 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n107.158.89.247 - - [26/Oct/2015:02:54:00 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n107.158.89.247 - - [26/Oct/2015:02:54:01 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n107.158.89.247 - - [26/Oct/2015:02:54:03 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n107.158.89.247 - - [26/Oct/2015:02:54:04 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n222.138.66.77 - - [26/Oct/2015:02:59:45 +0100] \"GET /join_form HTTP/1.0\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n222.138.66.77 - - [26/Oct/2015:02:59:58 +0100] \"POST /join_form HTTP/1.0\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n222.138.66.77 - - [26/Oct/2015:03:00:24 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.0\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n222.138.66.77 - - [26/Oct/2015:03:00:33 +0100] \"GET /login_form HTTP/1.0\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n222.138.66.77 - - [26/Oct/2015:03:00:38 +0100] \"POST /login_form HTTP/1.0\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n23.231.24.84 - - [26/Oct/2015:03:18:11 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36440 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n23.231.24.84 - - [26/Oct/2015:03:18:14 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n23.231.24.84 - - [26/Oct/2015:03:18:15 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n23.231.24.84 - - [26/Oct/2015:03:18:16 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n23.231.24.84 - - [26/Oct/2015:03:18:17 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n23.231.24.84 - - [26/Oct/2015:03:18:18 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n188.211.162.129 - - [26/Oct/2015:03:31:57 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basj.es/join_form HTTP/1.0\" 200 11713 \"http://niels.basj.es/\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n188.211.162.129 - - [26/Oct/2015:03:32:00 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basj.es/join_form HTTP/1.1\" 200 11713 \"http://niels.basj.es/\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n167.160.127.104 - - [26/Oct/2015:03:42:00 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n167.160.127.104 - - [26/Oct/2015:03:42:01 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n5.175.177.240 - - [26/Oct/2015:04:10:43 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basj.es/join_form HTTP/1.0\" 200 11713 \"http://niels.basj.es/\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n31.187.79.152 - - [26/Oct/2015:04:10:45 +0100] \"GET /join_form HTTP/1.0\" 200 12121 \"http://niels.basj.es/\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n31.187.79.152 - - [26/Oct/2015:04:10:45 +0100] \"POST /join_form HTTP/1.1\" 302 10098 \"http://niels.basj.es/join_form\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n31.187.79.152 - - [26/Oct/2015:04:10:45 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basj.es/join_form HTTP/1.1\" 200 11713 \"http://niels.basj.es/join_form\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n31.187.79.152 - - [26/Oct/2015:04:10:46 +0100] \"GET /login_form HTTP/1.1\" 200 11544 \"http://niels.basj.es/\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n31.187.79.152 - - [26/Oct/2015:04:10:46 +0100] \"POST /login_form HTTP/1.1\" 200 18237 \"http://niels.basj.es/login_form\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n93.152.141.25 - - [26/Oct/2015:04:12:53 +0100] \"GET / HTTP/1.1\" 302 285 \"http://www.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n93.152.141.25 - - [26/Oct/2015:04:12:53 +0100] \"GET / HTTP/1.1\" 200 20474 \"http://www.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n93.152.141.25 - - [26/Oct/2015:04:12:53 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n93.152.141.25 - - [26/Oct/2015:04:12:54 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n93.152.141.25 - - [26/Oct/2015:04:12:55 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n93.152.141.25 - - [26/Oct/2015:04:12:55 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n93.152.141.25 - - [26/Oct/2015:04:12:56 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n23.231.24.101 - - [26/Oct/2015:04:28:27 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36440 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n23.231.24.101 - - [26/Oct/2015:04:28:29 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n23.231.24.101 - - [26/Oct/2015:04:28:30 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n23.231.24.101 - - [26/Oct/2015:04:28:31 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n23.231.24.101 - - [26/Oct/2015:04:28:32 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n23.231.24.101 - - [26/Oct/2015:04:28:33 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n107.158.113.117 - - [26/Oct/2015:04:37:58 +0100] \"GET / HTTP/1.1\" 200 20474 \"http://niels.basjes.nl\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n107.158.113.117 - - [26/Oct/2015:04:37:59 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n107.158.113.117 - - [26/Oct/2015:04:38:01 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n107.158.113.117 - - [26/Oct/2015:04:38:02 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n107.158.113.117 - - [26/Oct/2015:04:38:03 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n107.158.113.117 - - [26/Oct/2015:04:38:04 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n107.161.83.180 - - [26/Oct/2015:04:43:07 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36440 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n107.161.83.180 - - [26/Oct/2015:04:43:09 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n107.161.83.180 - - [26/Oct/2015:04:43:10 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n107.161.83.180 - - [26/Oct/2015:04:43:11 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n107.161.83.180 - - [26/Oct/2015:04:43:12 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n107.161.83.180 - - [26/Oct/2015:04:43:13 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n93.152.141.25 - - [26/Oct/2015:04:44:34 +0100] \"GET / HTTP/1.1\" 302 285 \"http://www.basjes.nl\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n93.152.141.25 - - [26/Oct/2015:04:44:35 +0100] \"GET / HTTP/1.1\" 200 20474 \"http://www.basjes.nl\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n93.152.141.25 - - [26/Oct/2015:04:44:35 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n93.152.141.25 - - [26/Oct/2015:04:44:36 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n93.152.141.25 - - [26/Oct/2015:04:44:36 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n93.152.141.25 - - [26/Oct/2015:04:44:37 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n93.152.141.25 - - [26/Oct/2015:04:44:37 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n104.144.19.69 - - [26/Oct/2015:04:52:36 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.0\" 200 24319 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n104.144.19.69 - - [26/Oct/2015:04:52:39 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n104.144.19.69 - - [26/Oct/2015:04:52:40 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n104.144.19.69 - - [26/Oct/2015:04:52:42 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n104.144.19.69 - - [26/Oct/2015:04:52:43 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n104.144.19.69 - - [26/Oct/2015:04:52:45 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n23.95.238.253 - - [26/Oct/2015:05:11:36 +0100] \"GET / HTTP/1.0\" 200 17053 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n23.95.238.253 - - [26/Oct/2015:05:11:38 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n23.95.238.253 - - [26/Oct/2015:05:11:39 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n23.95.238.253 - - [26/Oct/2015:05:11:42 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n23.95.238.253 - - [26/Oct/2015:05:11:43 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n23.95.238.253 - - [26/Oct/2015:05:11:45 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n1.0.184.47 - - [26/Oct/2015:05:18:36 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.1\" 200 12881 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.158.196.24 - - [26/Oct/2015:05:21:23 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.0\" 200 24319 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n216.158.196.24 - - [26/Oct/2015:05:21:26 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n158.222.8.215 - - [26/Oct/2015:05:21:44 +0100] \"POST /join_form HTTP/1.0\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n64.110.135.40 - - [26/Oct/2015:05:38:02 +0100] \"GET /places HTTP/1.0\" 200 19355 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n64.110.135.40 - - [26/Oct/2015:05:38:03 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n64.110.135.40 - - [26/Oct/2015:05:38:05 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n64.110.135.40 - - [26/Oct/2015:05:38:06 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n64.110.135.40 - - [26/Oct/2015:05:38:09 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n64.110.135.40 - - [26/Oct/2015:05:38:10 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n1.0.184.47 - - [26/Oct/2015:05:47:11 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.1\" 200 24319 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n1.0.184.47 - - [26/Oct/2015:05:47:16 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n1.0.184.47 - - [26/Oct/2015:05:47:18 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n1.0.184.47 - - [26/Oct/2015:05:47:24 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n1.0.184.47 - - [26/Oct/2015:05:47:26 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n1.0.184.47 - - [26/Oct/2015:05:47:28 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n5.175.177.235 - - [26/Oct/2015:06:35:19 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.0\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n5.175.177.191 - - [26/Oct/2015:06:35:42 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.0\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n5.175.140.91 - - [26/Oct/2015:07:01:09 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.0\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n5.175.140.91 - - [26/Oct/2015:07:01:09 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n5.175.140.91 - - [26/Oct/2015:07:01:10 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n5.175.140.91 - - [26/Oct/2015:07:01:10 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n5.175.140.91 - - [26/Oct/2015:07:01:11 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n5.175.140.91 - - [26/Oct/2015:07:01:11 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n192.161.195.201 - - [26/Oct/2015:07:02:25 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n192.161.195.201 - - [26/Oct/2015:07:02:25 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n192.161.195.201 - - [26/Oct/2015:07:02:26 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n192.161.195.201 - - [26/Oct/2015:07:02:27 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n192.161.195.201 - - [26/Oct/2015:07:02:27 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n192.161.195.201 - - [26/Oct/2015:07:02:28 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n158.222.12.192 - - [26/Oct/2015:07:04:12 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36440 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n158.222.12.192 - - [26/Oct/2015:07:04:14 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n158.222.12.192 - - [26/Oct/2015:07:04:15 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n158.222.12.192 - - [26/Oct/2015:07:04:16 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n158.222.12.192 - - [26/Oct/2015:07:04:17 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n158.222.12.192 - - [26/Oct/2015:07:04:18 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n216.244.81.34 - - [26/Oct/2015:07:07:42 +0100] \"GET /search_form HTTP/1.1\" 200 29978 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n216.244.81.34 - - [26/Oct/2015:07:07:44 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n216.244.81.34 - - [26/Oct/2015:07:07:45 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n216.244.81.34 - - [26/Oct/2015:07:07:46 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n178.216.54.163 - - [26/Oct/2015:07:20:58 +0100] \"GET /join_form HTTP/1.0\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n178.216.54.163 - - [26/Oct/2015:07:20:59 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n178.216.54.163 - - [26/Oct/2015:07:21:00 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n178.216.54.163 - - [26/Oct/2015:07:21:02 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n178.216.54.163 - - [26/Oct/2015:07:21:03 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n216.244.81.34 - - [26/Oct/2015:07:27:32 +0100] \"GET /contact-info HTTP/1.1\" 200 15797 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n216.244.81.34 - - [26/Oct/2015:07:27:33 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n216.244.81.34 - - [26/Oct/2015:07:27:34 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n216.244.81.34 - - [26/Oct/2015:07:27:35 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n180.180.108.189 - - [26/Oct/2015:07:29:22 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.1\" 200 24319 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n180.180.108.189 - - [26/Oct/2015:07:29:25 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n180.180.108.189 - - [26/Oct/2015:07:29:26 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n180.180.108.189 - - [26/Oct/2015:07:29:28 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n180.180.108.189 - - [26/Oct/2015:07:29:30 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n180.180.108.189 - - [26/Oct/2015:07:29:32 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n66.150.34.161 - - [26/Oct/2015:07:33:31 +0100] \"GET / HTTP/1.1\" 200 20474 \"http://niels.basjes.nl\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n66.150.34.161 - - [26/Oct/2015:07:33:32 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n66.150.34.161 - - [26/Oct/2015:07:33:33 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n66.150.34.161 - - [26/Oct/2015:07:33:34 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n66.150.34.161 - - [26/Oct/2015:07:33:35 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n66.150.34.161 - - [26/Oct/2015:07:33:36 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n31.187.79.201 - - [26/Oct/2015:07:34:14 +0100] \"GET / HTTP/1.0\" 200 20474 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n31.187.79.201 - - [26/Oct/2015:07:34:14 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n31.187.79.201 - - [26/Oct/2015:07:34:15 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n31.187.79.201 - - [26/Oct/2015:07:34:16 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n31.187.79.201 - - [26/Oct/2015:07:34:16 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n31.187.79.201 - - [26/Oct/2015:07:34:16 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n216.244.81.34 - - [26/Oct/2015:07:44:37 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n216.244.81.34 - - [26/Oct/2015:07:44:38 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n216.244.81.34 - - [26/Oct/2015:07:44:39 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n216.244.81.34 - - [26/Oct/2015:07:44:40 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n31.220.113.224 - - [26/Oct/2015:07:46:15 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.0\" 200 24319 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n31.220.113.224 - - [26/Oct/2015:07:46:17 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n31.220.113.224 - - [26/Oct/2015:07:46:17 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n31.220.113.224 - - [26/Oct/2015:07:46:18 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n31.220.113.224 - - [26/Oct/2015:07:46:19 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n31.220.113.224 - - [26/Oct/2015:07:46:19 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n216.244.81.34 - - [26/Oct/2015:07:49:50 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n216.244.81.34 - - [26/Oct/2015:07:49:51 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n216.244.81.34 - - [26/Oct/2015:07:49:52 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n216.244.81.34 - - [26/Oct/2015:07:49:53 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 5.1; rv:35.0) Gecko/20100101 Firefox/35.0\"\n216.244.81.34 - - [26/Oct/2015:08:08:30 +0100] \"GET /contact-info HTTP/1.1\" 200 15797 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:08:08:31 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:08:08:32 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:08:08:33 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n180.180.108.189 - - [26/Oct/2015:08:54:18 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.1\" 200 24319 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n180.180.108.189 - - [26/Oct/2015:08:54:21 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n180.180.108.189 - - [26/Oct/2015:08:54:22 +0100] \"POST /join_form HTTP/1.1\" 302 9245 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n180.180.108.189 - - [26/Oct/2015:08:54:24 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n180.180.108.189 - - [26/Oct/2015:08:54:26 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n180.180.108.189 - - [26/Oct/2015:08:54:28 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n23.95.201.243 - - [26/Oct/2015:08:59:11 +0100] \"GET /places HTTP/1.0\" 200 19355 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n23.95.201.243 - - [26/Oct/2015:08:59:12 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n23.95.201.243 - - [26/Oct/2015:08:59:14 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n23.95.201.243 - - [26/Oct/2015:08:59:15 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n23.95.201.243 - - [26/Oct/2015:08:59:16 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n23.95.201.243 - - [26/Oct/2015:08:59:17 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:09:08:51 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:09:08:52 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:09:08:53 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:09:12:36 +0100] \"GET /splittable-gzip HTTP/1.1\" 200 27869 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:09:12:38 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:09:12:39 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:09:12:40 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:09:24:12 +0100] \"GET /friends HTTP/1.1\" 200 17524 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:09:24:13 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:09:24:14 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:09:24:15 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n198.12.82.174 - - [26/Oct/2015:09:25:51 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36440 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n198.12.82.174 - - [26/Oct/2015:09:25:53 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n198.12.82.174 - - [26/Oct/2015:09:25:54 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n198.12.82.174 - - [26/Oct/2015:09:25:54 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n198.12.82.174 - - [26/Oct/2015:09:25:55 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n198.12.82.174 - - [26/Oct/2015:09:25:56 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n107.173.209.101 - - [26/Oct/2015:09:26:37 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n107.173.209.101 - - [26/Oct/2015:09:26:38 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:09:26:40 +0100] \"GET /open-source HTTP/1.1\" 200 17525 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:09:26:41 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:09:26:42 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:09:26:43 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.158.199.158 - - [26/Oct/2015:09:30:19 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.0\" 200 24319 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:09:32:38 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:09:32:39 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:09:32:40 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n23.254.164.173 - - [26/Oct/2015:09:33:41 +0100] \"GET /linux/installing-my-new-server/Various-software/RK=0/RS=C_oF6PJOAKvJ3JsgiwAbhNKI2uE- HTTP/1.1\" 404 12145 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.64 Safari/537.31\"\n23.254.164.173 - - [26/Oct/2015:09:33:42 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/linux/installing-my-new-server/Various-software/RK=0/RS=C_oF6PJOAKvJ3JsgiwAbhNKI2uE-\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.64 Safari/537.31\"\n23.254.164.173 - - [26/Oct/2015:09:33:43 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.64 Safari/537.31\"\n23.254.164.173 - - [26/Oct/2015:09:33:44 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.64 Safari/537.31\"\n23.254.164.173 - - [26/Oct/2015:09:33:45 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.64 Safari/537.31\"\n23.254.164.173 - - [26/Oct/2015:09:33:45 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.64 Safari/537.31\"\n216.244.81.34 - - [26/Oct/2015:09:45:36 +0100] \"GET /friends HTTP/1.1\" 200 17524 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:09:45:38 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:09:45:39 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:09:45:39 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:09:49:10 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:09:49:11 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:09:49:12 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:09:54:25 +0100] \"GET /splittable-gzip HTTP/1.1\" 200 27869 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:09:54:27 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:09:54:28 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:09:54:29 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:09:54:47 +0100] \"GET /open-source HTTP/1.1\" 200 17525 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:09:54:48 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:09:54:49 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:09:54:50 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:09:57:53 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:09:57:54 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:09:57:55 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:09:58:48 +0100] \"GET /work HTTP/1.1\" 200 17881 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:09:58:49 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:09:58:50 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:09:58:51 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n104.128.23.51 - - [26/Oct/2015:10:05:50 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.0\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n104.128.23.51 - - [26/Oct/2015:10:05:52 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:10:08:10 +0100] \"GET /accessibility-info HTTP/1.1\" 200 20622 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:10:08:11 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:10:08:12 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:10:08:13 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:10:14:45 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:10:14:46 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:10:14:47 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:10:21:36 +0100] \"GET /work HTTP/1.1\" 200 17881 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:10:21:38 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:10:21:39 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:10:21:40 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:10:42:45 +0100] \"GET /accessibility-info HTTP/1.1\" 200 20622 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:10:42:47 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:10:42:48 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:10:42:49 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:10:51:02 +0100] \"GET /sitemap HTTP/1.1\" 200 12065 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:10:51:03 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:10:51:04 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:10:51:04 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n180.180.107.255 - - [26/Oct/2015:10:53:20 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.1\" 200 24319 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n180.180.107.255 - - [26/Oct/2015:10:53:25 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n180.180.107.255 - - [26/Oct/2015:10:53:26 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n180.180.107.255 - - [26/Oct/2015:10:53:28 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n180.180.107.255 - - [26/Oct/2015:10:53:29 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n180.180.107.255 - - [26/Oct/2015:10:53:30 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n61.162.184.21 - - [26/Oct/2015:11:07:37 +0100] \"GET /join_form HTTP/1.0\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n91.217.42.1 - - [26/Oct/2015:11:08:02 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:11:14:22 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:11:14:23 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:11:14:23 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n167.160.127.184 - - [26/Oct/2015:11:19:51 +0100] \"GET /linux/installing-my-new-server/vmware-server HTTP/1.1\" 200 25005 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n167.160.127.184 - - [26/Oct/2015:11:19:52 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n167.160.127.184 - - [26/Oct/2015:11:19:53 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n167.160.127.184 - - [26/Oct/2015:11:19:54 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:11:20:41 +0100] \"GET /sitemap HTTP/1.1\" 200 12065 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:11:20:42 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:11:20:43 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:11:20:44 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n104.254.212.109 - - [26/Oct/2015:11:20:50 +0100] \"GET / HTTP/1.1\" 200 20474 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n104.254.212.109 - - [26/Oct/2015:11:20:51 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n104.254.212.109 - - [26/Oct/2015:11:20:52 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n104.254.212.109 - - [26/Oct/2015:11:20:53 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n104.254.212.109 - - [26/Oct/2015:11:20:55 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n104.254.212.109 - - [26/Oct/2015:11:20:56 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n104.223.45.40 - - [26/Oct/2015:11:22:45 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n104.223.45.40 - - [26/Oct/2015:11:22:46 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:11:26:10 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:11:26:11 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:11:26:12 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:11:33:07 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:11:33:08 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:11:33:09 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n173.254.249.15 - - [26/Oct/2015:11:34:47 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n173.254.249.15 - - [26/Oct/2015:11:34:48 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:11:37:31 +0100] \"GET / HTTP/1.1\" 302 281 \"http://basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:11:37:32 +0100] \"GET / HTTP/1.1\" 200 20474 \"http://basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:11:37:33 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:11:37:34 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:11:37:35 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n104.140.71.84 - - [26/Oct/2015:11:47:39 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n104.140.71.84 - - [26/Oct/2015:11:47:40 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n104.140.71.84 - - [26/Oct/2015:11:47:41 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n104.140.71.84 - - [26/Oct/2015:11:47:42 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n104.140.71.84 - - [26/Oct/2015:11:47:43 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n104.140.71.84 - - [26/Oct/2015:11:47:44 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:11:53:40 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:11:53:41 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:11:53:42 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n220.181.143.14 - - [26/Oct/2015:11:54:22 +0100] \"GET /join_form HTTP/1.0\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n220.181.143.14 - - [26/Oct/2015:11:54:24 +0100] \"POST /join_form HTTP/1.0\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n220.181.143.14 - - [26/Oct/2015:11:54:26 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.0\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:11:59:05 +0100] \"GET / HTTP/1.1\" 302 281 \"http://basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:11:59:05 +0100] \"GET / HTTP/1.1\" 200 20474 \"http://basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n212.83.183.34 - - [26/Oct/2015:11:59:05 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36440 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:11:59:07 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:11:59:08 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:11:59:09 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:11:59:20 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:11:59:21 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:11:59:22 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:11:59:39 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:11:59:40 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:11:59:41 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:12:02:31 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:12:02:32 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:12:02:32 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n158.222.15.195 - - [26/Oct/2015:12:06:17 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n158.222.15.195 - - [26/Oct/2015:12:06:18 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n101.71.27.120 - - [26/Oct/2015:12:06:41 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.0\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n101.71.27.120 - - [26/Oct/2015:12:06:44 +0100] \"GET /login_form HTTP/1.0\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n101.71.27.120 - - [26/Oct/2015:12:06:47 +0100] \"POST /login_form HTTP/1.0\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:12:14:03 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:12:14:04 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:12:14:05 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n180.180.116.194 - - [26/Oct/2015:12:27:15 +0100] \"GET / HTTP/1.1\" 200 20474 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n180.180.116.194 - - [26/Oct/2015:12:27:17 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n180.180.116.194 - - [26/Oct/2015:12:27:19 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n180.180.116.194 - - [26/Oct/2015:12:27:20 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n180.180.116.194 - - [26/Oct/2015:12:27:22 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n180.180.116.194 - - [26/Oct/2015:12:27:24 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:12:34:00 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:12:34:01 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:12:34:02 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n23.254.164.173 - - [26/Oct/2015:12:41:09 +0100] \"GET /linux/installing-my-new-server/Various-software/RK=0/RS=C_oF6PJOAKvJ3JsgiwAbhNKI2uE- HTTP/1.1\" 404 12145 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.64 Safari/537.31\"\n23.254.164.173 - - [26/Oct/2015:12:41:10 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/linux/installing-my-new-server/Various-software/RK=0/RS=C_oF6PJOAKvJ3JsgiwAbhNKI2uE-\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.64 Safari/537.31\"\n23.254.164.173 - - [26/Oct/2015:12:41:15 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.64 Safari/537.31\"\n23.254.164.173 - - [26/Oct/2015:12:41:17 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.64 Safari/537.31\"\n23.254.164.173 - - [26/Oct/2015:12:41:18 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.64 Safari/537.31\"\n23.254.164.173 - - [26/Oct/2015:12:41:21 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.64 Safari/537.31\"\n158.222.15.194 - - [26/Oct/2015:12:43:48 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n158.222.15.194 - - [26/Oct/2015:12:43:49 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n158.222.15.194 - - [26/Oct/2015:12:43:50 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:12:45:04 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:12:45:05 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:12:45:06 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n1.0.191.52 - - [26/Oct/2015:12:45:44 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.1\" 200 24319 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n1.0.191.52 - - [26/Oct/2015:12:45:46 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n1.0.191.52 - - [26/Oct/2015:12:45:49 +0100] \"POST /join_form HTTP/1.1\" 302 9245 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n1.0.191.52 - - [26/Oct/2015:12:45:50 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n1.0.191.52 - - [26/Oct/2015:12:45:52 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n1.0.191.52 - - [26/Oct/2015:12:45:54 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:12:51:04 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:12:51:05 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:12:51:06 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n5.175.140.91 - - [26/Oct/2015:13:03:59 +0100] \"GET /linux/installing-gitlab-on-centos-6 HTTP/1.0\" 200 19418 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n5.175.140.91 - - [26/Oct/2015:13:04:01 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n5.175.140.91 - - [26/Oct/2015:13:04:02 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n148.251.65.34 - - [26/Oct/2015:13:13:13 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36440 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n158.222.15.61 - - [26/Oct/2015:13:13:14 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n167.160.110.250 - - [26/Oct/2015:13:13:16 +0100] \"POST /join_form HTTP/1.0\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n167.160.110.250 - - [26/Oct/2015:13:13:18 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.0\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n167.160.110.250 - - [26/Oct/2015:13:13:19 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n167.160.110.250 - - [26/Oct/2015:13:13:20 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:13:29:01 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:13:29:02 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:13:29:03 +0100] \"POST /login_form HTTP/1.1\" 200 18398 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n14.139.172.170 - - [26/Oct/2015:13:33:55 +0100] \"GET /linux/installing-my-new-server/networking HTTP/1.1\" 200 27448 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n14.139.172.170 - - [26/Oct/2015:13:33:58 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n14.139.172.170 - - [26/Oct/2015:13:34:00 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n14.139.172.170 - - [26/Oct/2015:13:34:01 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n107.161.83.180 - - [26/Oct/2015:13:43:33 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36440 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n107.161.83.180 - - [26/Oct/2015:13:43:36 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n107.161.83.180 - - [26/Oct/2015:13:43:37 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n107.161.83.180 - - [26/Oct/2015:13:43:38 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n107.161.83.180 - - [26/Oct/2015:13:43:39 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n107.161.83.180 - - [26/Oct/2015:13:43:40 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n216.244.81.34 - - [26/Oct/2015:13:44:22 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:13:44:23 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:13:44:24 +0100] \"POST /login_form HTTP/1.1\" 200 18398 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n24.157.37.61 - - [26/Oct/2015:13:52:46 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n24.157.37.61 - - [26/Oct/2015:13:52:47 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n24.157.37.61 - - [26/Oct/2015:13:52:47 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:14:09:16 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:14:09:17 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.244.81.34 - - [26/Oct/2015:14:09:18 +0100] \"POST /login_form HTTP/1.1\" 200 18398 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n52.11.126.199 - - [26/Oct/2015:14:11:51 +0100] \"GET /linux/installing-my-new-server/networking HTTP/1.0\" 200 27448 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n52.11.126.199 - - [26/Oct/2015:14:11:53 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n52.11.126.199 - - [26/Oct/2015:14:11:55 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n52.11.126.199 - - [26/Oct/2015:14:11:56 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n52.11.126.199 - - [26/Oct/2015:14:11:57 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n52.11.126.199 - - [26/Oct/2015:14:11:59 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n192.126.192.238 - - [26/Oct/2015:14:12:20 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.0\" 200 24319 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n192.126.192.238 - - [26/Oct/2015:14:12:22 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n192.126.192.238 - - [26/Oct/2015:14:12:24 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n192.126.192.238 - - [26/Oct/2015:14:12:25 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n192.126.192.238 - - [26/Oct/2015:14:12:27 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n192.126.192.238 - - [26/Oct/2015:14:12:29 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n192.119.149.217 - - [26/Oct/2015:14:22:25 +0100] \"GET / HTTP/1.1\" 200 20474 \"http://niels.basjes.nl\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n192.119.149.217 - - [26/Oct/2015:14:22:26 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n192.119.149.217 - - [26/Oct/2015:14:22:27 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n192.119.149.217 - - [26/Oct/2015:14:22:28 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n192.119.149.217 - - [26/Oct/2015:14:22:29 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n192.119.149.217 - - [26/Oct/2015:14:22:29 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n198.23.234.158 - - [26/Oct/2015:14:28:23 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.0\" 200 36440 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n198.23.234.158 - - [26/Oct/2015:14:28:25 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n198.23.234.158 - - [26/Oct/2015:14:28:26 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n198.23.234.158 - - [26/Oct/2015:14:28:27 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n198.23.234.158 - - [26/Oct/2015:14:28:29 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n198.23.234.158 - - [26/Oct/2015:14:28:30 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n173.208.103.56 - - [26/Oct/2015:14:35:23 +0100] \"GET /join_form HTTP/1.0\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n173.208.103.56 - - [26/Oct/2015:14:35:31 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n173.208.103.56 - - [26/Oct/2015:14:35:36 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n173.208.103.56 - - [26/Oct/2015:14:35:42 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n173.208.103.56 - - [26/Oct/2015:14:35:44 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n62.210.204.180 - - [26/Oct/2015:14:38:58 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n62.210.204.180 - - [26/Oct/2015:14:38:58 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n62.210.204.180 - - [26/Oct/2015:14:38:59 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n62.210.204.180 - - [26/Oct/2015:14:38:59 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n62.210.204.180 - - [26/Oct/2015:14:39:00 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n62.210.204.180 - - [26/Oct/2015:14:39:00 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n69.46.64.183 - - [26/Oct/2015:14:54:58 +0100] \"GET / HTTP/1.0\" 200 20474 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n69.46.64.183 - - [26/Oct/2015:14:55:00 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n69.46.64.183 - - [26/Oct/2015:14:55:01 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n69.46.64.183 - - [26/Oct/2015:14:55:03 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n69.46.64.183 - - [26/Oct/2015:14:55:04 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n69.46.64.183 - - [26/Oct/2015:14:55:06 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n1.0.182.20 - - [26/Oct/2015:15:55:01 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.1\" 200 24319 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n1.0.182.20 - - [26/Oct/2015:15:55:09 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n1.0.182.20 - - [26/Oct/2015:15:55:11 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n1.0.182.20 - - [26/Oct/2015:15:55:13 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n1.0.182.20 - - [26/Oct/2015:15:55:15 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n1.0.182.20 - - [26/Oct/2015:15:55:17 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n180.180.113.146 - - [26/Oct/2015:15:57:35 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.1\" 200 24319 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n180.180.113.146 - - [26/Oct/2015:15:57:37 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n180.180.113.146 - - [26/Oct/2015:15:57:39 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n180.180.113.146 - - [26/Oct/2015:15:57:40 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n180.180.113.146 - - [26/Oct/2015:15:57:42 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n180.180.113.146 - - [26/Oct/2015:15:57:43 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n94.23.33.25 - - [26/Oct/2015:16:07:26 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36440 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n204.44.112.144 - - [26/Oct/2015:16:07:29 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n204.44.112.144 - - [26/Oct/2015:16:07:31 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n204.44.112.144 - - [26/Oct/2015:16:07:32 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n204.44.112.144 - - [26/Oct/2015:16:07:34 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n204.44.112.144 - - [26/Oct/2015:16:07:38 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n180.180.70.177 - - [26/Oct/2015:16:35:47 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.1\" 200 24319 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n180.180.70.177 - - [26/Oct/2015:16:35:50 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n180.180.70.177 - - [26/Oct/2015:16:35:51 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n180.180.70.177 - - [26/Oct/2015:16:35:53 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n180.180.70.177 - - [26/Oct/2015:16:35:55 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n180.180.70.177 - - [26/Oct/2015:16:35:56 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n212.83.141.47 - - [26/Oct/2015:16:46:56 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n158.222.14.51 - - [26/Oct/2015:16:49:10 +0100] \"GET /linux/installing-my-new-server/vmware-server HTTP/1.0\" 200 25005 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n158.222.14.51 - - [26/Oct/2015:16:49:12 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n158.222.14.51 - - [26/Oct/2015:16:49:14 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n158.222.14.51 - - [26/Oct/2015:16:49:16 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n158.222.14.51 - - [26/Oct/2015:16:49:18 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n158.222.14.51 - - [26/Oct/2015:16:49:20 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n5.138.121.115 - - [26/Oct/2015:16:51:15 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n1.0.182.20 - - [26/Oct/2015:17:01:05 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.1\" 200 24319 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n1.0.182.20 - - [26/Oct/2015:17:01:08 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n1.0.182.20 - - [26/Oct/2015:17:01:11 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n1.0.182.20 - - [26/Oct/2015:17:01:13 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n1.0.182.20 - - [26/Oct/2015:17:01:15 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n1.0.182.20 - - [26/Oct/2015:17:01:17 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n94.23.33.25 - - [26/Oct/2015:17:01:48 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36440 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n107.158.89.239 - - [26/Oct/2015:17:01:49 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n107.158.89.239 - - [26/Oct/2015:17:01:51 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n107.158.89.239 - - [26/Oct/2015:17:01:52 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n107.158.89.239 - - [26/Oct/2015:17:01:54 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n192.3.106.205 - - [26/Oct/2015:17:01:55 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36440 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n107.158.89.239 - - [26/Oct/2015:17:01:56 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n192.3.106.205 - - [26/Oct/2015:17:01:57 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n192.3.106.205 - - [26/Oct/2015:17:01:58 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n192.3.106.205 - - [26/Oct/2015:17:01:59 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n192.3.106.205 - - [26/Oct/2015:17:02:00 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n192.3.106.205 - - [26/Oct/2015:17:02:01 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n23.254.164.173 - - [26/Oct/2015:17:02:14 +0100] \"GET /linux/installing-my-new-server/Various-software/RK=0/RS=C_oF6PJOAKvJ3JsgiwAbhNKI2uE- HTTP/1.1\" 404 12145 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.64 Safari/537.31\"\n23.254.164.173 - - [26/Oct/2015:17:02:15 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/linux/installing-my-new-server/Various-software/RK=0/RS=C_oF6PJOAKvJ3JsgiwAbhNKI2uE-\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.64 Safari/537.31\"\n23.254.164.173 - - [26/Oct/2015:17:02:16 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.64 Safari/537.31\"\n23.254.164.173 - - [26/Oct/2015:17:02:17 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.64 Safari/537.31\"\n23.254.164.173 - - [26/Oct/2015:17:02:19 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.64 Safari/537.31\"\n23.254.164.173 - - [26/Oct/2015:17:02:21 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.64 Safari/537.31\"\n172.245.225.213 - - [26/Oct/2015:17:16:51 +0100] \"GET / HTTP/1.1\" 200 20474 \"http://niels.basjes.nl\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n172.245.225.213 - - [26/Oct/2015:17:16:53 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n172.245.225.213 - - [26/Oct/2015:17:16:54 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n172.245.225.213 - - [26/Oct/2015:17:16:55 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n172.245.225.213 - - [26/Oct/2015:17:16:56 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n172.245.225.213 - - [26/Oct/2015:17:16:57 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n198.23.234.158 - - [26/Oct/2015:17:17:30 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.0\" 200 36440 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n198.23.234.158 - - [26/Oct/2015:17:17:32 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n198.23.234.158 - - [26/Oct/2015:17:17:33 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n198.23.234.158 - - [26/Oct/2015:17:17:34 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n198.23.234.158 - - [26/Oct/2015:17:17:36 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n198.23.234.158 - - [26/Oct/2015:17:17:37 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n198.55.104.205 - - [26/Oct/2015:17:18:27 +0100] \"GET /linux/installing-my-new-server/networking HTTP/1.0\" 200 27448 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n198.55.104.205 - - [26/Oct/2015:17:18:28 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n198.55.104.205 - - [26/Oct/2015:17:18:29 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n198.55.104.205 - - [26/Oct/2015:17:18:30 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n192.227.222.207 - - [26/Oct/2015:17:23:42 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n192.227.222.207 - - [26/Oct/2015:17:23:42 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n173.254.249.15 - - [26/Oct/2015:17:50:34 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n173.254.249.15 - - [26/Oct/2015:17:50:35 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n23.94.170.4 - - [26/Oct/2015:18:07:40 +0100] \"GET /places HTTP/1.0\" 200 19355 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n23.94.170.4 - - [26/Oct/2015:18:07:41 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n23.94.170.4 - - [26/Oct/2015:18:07:43 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n23.94.170.4 - - [26/Oct/2015:18:07:45 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n23.94.170.4 - - [26/Oct/2015:18:07:46 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n23.94.170.4 - - [26/Oct/2015:18:07:47 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n198.12.82.177 - - [26/Oct/2015:18:25:35 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36440 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n198.12.82.177 - - [26/Oct/2015:18:25:38 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n198.12.82.177 - - [26/Oct/2015:18:25:40 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n198.12.82.177 - - [26/Oct/2015:18:25:41 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n198.12.82.177 - - [26/Oct/2015:18:25:42 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n198.12.82.177 - - [26/Oct/2015:18:25:43 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n31.187.79.31 - - [26/Oct/2015:18:34:27 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basj.es/join_form HTTP/1.0\" 200 11713 \"http://niels.basj.es/\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.99 Safari/537.36\"\n31.187.79.31 - - [26/Oct/2015:18:34:28 +0100] \"GET /join_form HTTP/1.1\" 200 12121 \"http://niels.basj.es/\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.99 Safari/537.36\"\n31.187.79.31 - - [26/Oct/2015:18:34:28 +0100] \"POST /join_form HTTP/1.1\" 302 10098 \"http://niels.basj.es/join_form\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.99 Safari/537.36\"\n31.187.79.31 - - [26/Oct/2015:18:34:34 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basj.es/join_form HTTP/1.1\" 200 11713 \"http://niels.basj.es/join_form\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.99 Safari/537.36\"\n31.187.79.31 - - [26/Oct/2015:18:34:34 +0100] \"GET /login_form HTTP/1.1\" 200 11544 \"http://niels.basj.es/\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.99 Safari/537.36\"\n31.187.79.31 - - [26/Oct/2015:18:34:35 +0100] \"POST /login_form HTTP/1.1\" 200 18289 \"http://niels.basj.es/login_form\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.99 Safari/537.36\"\n117.177.243.42 - - [26/Oct/2015:19:07:55 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n117.177.243.42 - - [26/Oct/2015:19:07:58 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n117.177.243.42 - - [26/Oct/2015:19:08:00 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n117.177.243.42 - - [26/Oct/2015:19:08:01 +0100] \"POST /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n202.119.25.69 - - [26/Oct/2015:19:08:31 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n202.119.25.69 - - [26/Oct/2015:19:08:34 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n202.119.25.69 - - [26/Oct/2015:19:08:36 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n202.119.25.69 - - [26/Oct/2015:19:08:39 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n45.33.149.141 - - [26/Oct/2015:19:18:36 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.0\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n45.33.149.141 - - [26/Oct/2015:19:18:37 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n45.33.149.141 - - [26/Oct/2015:19:18:38 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n104.144.18.52 - - [26/Oct/2015:19:20:50 +0100] \"GET /join_form HTTP/1.0\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n104.144.18.52 - - [26/Oct/2015:19:20:52 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n104.144.18.52 - - [26/Oct/2015:19:20:54 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n104.144.18.52 - - [26/Oct/2015:19:20:58 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n104.144.18.52 - - [26/Oct/2015:19:21:01 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n5.157.42.183 - - [26/Oct/2015:19:29:33 +0100] \"GET / HTTP/1.0\" 200 20474 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n5.157.42.183 - - [26/Oct/2015:19:29:37 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n5.157.42.183 - - [26/Oct/2015:19:29:41 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n5.157.42.183 - - [26/Oct/2015:19:29:44 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n5.157.42.183 - - [26/Oct/2015:19:29:49 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n5.157.42.183 - - [26/Oct/2015:19:29:55 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n93.179.89.37 - - [26/Oct/2015:19:54:48 +0100] \"GET / HTTP/1.0\" 200 20474 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n93.179.89.37 - - [26/Oct/2015:19:54:49 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n93.179.89.37 - - [26/Oct/2015:19:54:50 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n93.179.89.37 - - [26/Oct/2015:19:54:51 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n93.179.89.37 - - [26/Oct/2015:19:54:52 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n93.179.89.37 - - [26/Oct/2015:19:54:53 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n192.99.244.139 - - [26/Oct/2015:20:18:57 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36440 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n204.152.206.246 - - [26/Oct/2015:20:28:59 +0100] \"GET /splittable-gzip HTTP/1.0\" 200 27869 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n204.152.206.246 - - [26/Oct/2015:20:29:01 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n204.152.206.246 - - [26/Oct/2015:20:29:03 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n204.152.206.246 - - [26/Oct/2015:20:29:05 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n204.152.206.246 - - [26/Oct/2015:20:29:07 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n204.152.206.246 - - [26/Oct/2015:20:29:08 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n94.23.33.25 - - [26/Oct/2015:20:42:54 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36440 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n107.158.89.204 - - [26/Oct/2015:20:42:55 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n107.158.89.204 - - [26/Oct/2015:20:42:56 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n107.158.89.204 - - [26/Oct/2015:20:42:58 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n107.158.89.204 - - [26/Oct/2015:20:42:59 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n107.158.89.204 - - [26/Oct/2015:20:43:00 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n158.222.15.194 - - [26/Oct/2015:20:45:45 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n158.222.15.194 - - [26/Oct/2015:20:45:47 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n216.158.196.211 - - [26/Oct/2015:21:20:04 +0100] \"GET / HTTP/1.1\" 200 20474 \"http://niels.basjes.nl\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n216.158.196.211 - - [26/Oct/2015:21:20:06 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n216.158.196.211 - - [26/Oct/2015:21:20:08 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n216.158.196.211 - - [26/Oct/2015:21:20:09 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n216.158.196.211 - - [26/Oct/2015:21:20:10 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n216.158.196.211 - - [26/Oct/2015:21:20:11 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n46.102.99.22 - - [26/Oct/2015:21:26:43 +0100] \"GET /author/nielsbasjes HTTP/1.0\" 200 15519 \"http://niels.basj.es/\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.99 Safari/537.36\"\n46.102.99.22 - - [26/Oct/2015:21:26:45 +0100] \"GET /join_form HTTP/1.1\" 200 12121 \"http://niels.basj.es/\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.99 Safari/537.36\"\n46.102.99.22 - - [26/Oct/2015:21:26:46 +0100] \"POST /join_form HTTP/1.1\" 302 10098 \"http://niels.basj.es/join_form\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.99 Safari/537.36\"\n46.102.99.22 - - [26/Oct/2015:21:26:47 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basj.es/join_form HTTP/1.1\" 200 11713 \"http://niels.basj.es/join_form\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.99 Safari/537.36\"\n46.102.99.22 - - [26/Oct/2015:21:26:53 +0100] \"GET /login_form HTTP/1.1\" 200 11544 \"http://niels.basj.es/\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.99 Safari/537.36\"\n46.102.99.22 - - [26/Oct/2015:21:26:54 +0100] \"POST /login_form HTTP/1.1\" 200 18237 \"http://niels.basj.es/login_form\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.99 Safari/537.36\"\n31.222.197.118 - - [26/Oct/2015:21:29:58 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.0\" 200 36440 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n31.222.197.118 - - [26/Oct/2015:21:29:59 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n31.222.197.118 - - [26/Oct/2015:21:30:00 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n31.222.197.118 - - [26/Oct/2015:21:30:00 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n110.52.149.19 - - [26/Oct/2015:21:31:30 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.0\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n110.52.149.19 - - [26/Oct/2015:21:31:32 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n192.99.244.139 - - [26/Oct/2015:21:43:01 +0100] \"GET /accessibility-info HTTP/1.1\" 200 20622 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n172.245.229.166 - - [26/Oct/2015:22:53:40 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n172.245.229.166 - - [26/Oct/2015:22:53:41 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n192.99.244.139 - - [26/Oct/2015:22:54:34 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36440 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n180.180.72.156 - - [26/Oct/2015:23:10:58 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.1\" 200 24319 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n180.180.72.156 - - [26/Oct/2015:23:11:02 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n180.180.72.156 - - [26/Oct/2015:23:11:04 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n180.180.72.156 - - [26/Oct/2015:23:11:06 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n180.180.72.156 - - [26/Oct/2015:23:11:08 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n180.180.72.156 - - [26/Oct/2015:23:11:10 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n183.120.167.25 - - [26/Oct/2015:23:29:24 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.0\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n183.120.167.25 - - [26/Oct/2015:23:29:26 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n183.120.167.25 - - [26/Oct/2015:23:29:28 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n180.180.69.206 - - [26/Oct/2015:23:50:07 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.1\" 200 24319 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n180.180.69.206 - - [26/Oct/2015:23:50:11 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n180.180.69.206 - - [26/Oct/2015:23:50:12 +0100] \"POST /join_form HTTP/1.1\" 302 9245 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n180.180.69.206 - - [26/Oct/2015:23:50:16 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n180.180.69.206 - - [26/Oct/2015:23:50:17 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n180.180.69.206 - - [26/Oct/2015:23:50:19 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n192.99.244.139 - - [27/Oct/2015:00:05:51 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36440 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n23.254.164.173 - - [27/Oct/2015:00:28:37 +0100] \"GET /linux/installing-my-new-server/voice-over-ip/RK=0/RS=tDIUMWhLOlRs5FUbKMPumYVN.f0- HTTP/1.1\" 404 12146 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.64 Safari/537.31\"\n23.254.164.173 - - [27/Oct/2015:00:28:38 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/linux/installing-my-new-server/voice-over-ip/RK=0/RS=tDIUMWhLOlRs5FUbKMPumYVN.f0-\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.64 Safari/537.31\"\n23.254.164.173 - - [27/Oct/2015:00:28:39 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.64 Safari/537.31\"\n23.254.164.173 - - [27/Oct/2015:00:28:40 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.64 Safari/537.31\"\n23.254.164.173 - - [27/Oct/2015:00:28:41 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.64 Safari/537.31\"\n23.254.164.173 - - [27/Oct/2015:00:28:41 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.64 Safari/537.31\"\n192.161.239.229 - - [27/Oct/2015:00:34:41 +0100] \"GET /join_form HTTP/1.0\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n192.161.239.229 - - [27/Oct/2015:00:34:43 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n192.161.239.229 - - [27/Oct/2015:00:34:44 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n192.161.239.229 - - [27/Oct/2015:00:34:45 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n192.161.239.229 - - [27/Oct/2015:00:34:46 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n192.119.149.176 - - [27/Oct/2015:00:35:12 +0100] \"GET / HTTP/1.1\" 200 20474 \"http://niels.basjes.nl\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n192.119.149.176 - - [27/Oct/2015:00:35:13 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n192.119.149.176 - - [27/Oct/2015:00:35:14 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n192.119.149.176 - - [27/Oct/2015:00:35:14 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n192.119.149.176 - - [27/Oct/2015:00:35:15 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n192.119.149.176 - - [27/Oct/2015:00:35:16 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n180.180.75.99 - - [27/Oct/2015:00:35:57 +0100] \"GET / HTTP/1.1\" 200 20474 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n180.180.75.99 - - [27/Oct/2015:00:35:59 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n180.180.75.99 - - [27/Oct/2015:00:36:01 +0100] \"POST /join_form HTTP/1.1\" 302 10322 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n180.180.75.99 - - [27/Oct/2015:00:36:02 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n180.180.75.99 - - [27/Oct/2015:00:36:04 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n180.180.75.99 - - [27/Oct/2015:00:36:06 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n172.245.229.16 - - [27/Oct/2015:01:28:12 +0100] \"GET /linux/installing-my-new-server/networking HTTP/1.0\" 200 27448 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n172.245.229.16 - - [27/Oct/2015:01:28:13 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n172.245.229.16 - - [27/Oct/2015:01:28:15 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n172.245.229.16 - - [27/Oct/2015:01:28:15 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n180.180.109.46 - - [27/Oct/2015:01:57:45 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.1\" 200 24319 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n180.180.109.46 - - [27/Oct/2015:01:57:48 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n180.180.109.46 - - [27/Oct/2015:01:57:51 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n180.180.109.46 - - [27/Oct/2015:01:57:56 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n180.180.109.46 - - [27/Oct/2015:01:57:59 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n180.180.109.46 - - [27/Oct/2015:01:58:00 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n212.108.129.22 - - [27/Oct/2015:02:05:54 +0100] \"GET /places HTTP/1.0\" 200 19355 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n212.108.129.22 - - [27/Oct/2015:02:05:55 +0100] \"GET /join_form HTTP/1.0\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n212.108.129.22 - - [27/Oct/2015:02:05:56 +0100] \"POST /join_form HTTP/1.0\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n212.108.129.22 - - [27/Oct/2015:02:05:58 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.0\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n212.108.129.22 - - [27/Oct/2015:02:05:59 +0100] \"GET /login_form HTTP/1.0\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n212.108.129.22 - - [27/Oct/2015:02:06:02 +0100] \"POST /login_form HTTP/1.0\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n95.154.210.132 - - [27/Oct/2015:02:09:25 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n95.154.210.132 - - [27/Oct/2015:02:09:25 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n23.94.119.76 - - [27/Oct/2015:02:19:08 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.0\" 200 36440 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n23.94.119.76 - - [27/Oct/2015:02:19:10 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n23.94.119.76 - - [27/Oct/2015:02:19:12 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n23.94.119.76 - - [27/Oct/2015:02:19:13 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n23.94.119.76 - - [27/Oct/2015:02:19:14 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n23.94.119.76 - - [27/Oct/2015:02:19:15 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n62.210.204.180 - - [27/Oct/2015:03:03:21 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n62.210.204.180 - - [27/Oct/2015:03:03:22 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n62.210.204.180 - - [27/Oct/2015:03:03:22 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n62.210.204.180 - - [27/Oct/2015:03:03:22 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n62.210.204.180 - - [27/Oct/2015:03:03:24 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n62.210.204.180 - - [27/Oct/2015:03:03:24 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n45.33.146.8 - - [27/Oct/2015:03:14:22 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36440 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n45.33.146.8 - - [27/Oct/2015:03:14:23 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n45.33.146.8 - - [27/Oct/2015:03:14:25 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n45.33.146.8 - - [27/Oct/2015:03:14:27 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n45.33.146.8 - - [27/Oct/2015:03:14:29 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n45.33.146.8 - - [27/Oct/2015:03:14:31 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n89.35.104.119 - - [27/Oct/2015:03:28:42 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.0\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n89.35.104.119 - - [27/Oct/2015:03:28:43 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n89.35.104.119 - - [27/Oct/2015:03:28:45 +0100] \"POST /login_form HTTP/1.1\" 200 16854 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n180.180.114.7 - - [27/Oct/2015:03:29:24 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.1\" 200 24367 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n180.180.114.7 - - [27/Oct/2015:03:29:27 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n180.180.114.7 - - [27/Oct/2015:03:29:30 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n180.180.114.7 - - [27/Oct/2015:03:29:31 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n180.180.114.7 - - [27/Oct/2015:03:29:33 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n180.180.114.7 - - [27/Oct/2015:03:29:34 +0100] \"POST /login_form HTTP/1.1\" 200 16854 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n188.210.215.242 - - [27/Oct/2015:03:37:26 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.0\" 200 24367 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n188.210.215.242 - - [27/Oct/2015:03:37:27 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n188.210.215.242 - - [27/Oct/2015:03:37:28 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n188.210.215.242 - - [27/Oct/2015:03:37:29 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n66.150.34.157 - - [27/Oct/2015:04:04:04 +0100] \"GET / HTTP/1.1\" 200 20474 \"http://niels.basjes.nl\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n66.150.34.157 - - [27/Oct/2015:04:04:06 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n66.150.34.157 - - [27/Oct/2015:04:04:07 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n66.150.34.157 - - [27/Oct/2015:04:04:08 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n66.150.34.157 - - [27/Oct/2015:04:04:08 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n66.150.34.157 - - [27/Oct/2015:04:04:09 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n23.231.24.17 - - [27/Oct/2015:04:17:31 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36488 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n23.231.24.17 - - [27/Oct/2015:04:17:33 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n23.231.24.17 - - [27/Oct/2015:04:17:34 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n23.231.24.17 - - [27/Oct/2015:04:17:35 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n23.231.24.17 - - [27/Oct/2015:04:17:36 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n23.231.24.17 - - [27/Oct/2015:04:17:37 +0100] \"POST /login_form HTTP/1.1\" 200 16854 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\"\n198.23.234.158 - - [27/Oct/2015:04:45:56 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.0\" 200 36440 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n198.23.234.158 - - [27/Oct/2015:04:45:58 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n198.23.234.158 - - [27/Oct/2015:04:46:00 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n198.23.234.158 - - [27/Oct/2015:04:46:01 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n198.23.234.158 - - [27/Oct/2015:04:46:03 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n198.23.234.158 - - [27/Oct/2015:04:46:05 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n23.94.80.209 - - [27/Oct/2015:04:47:00 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.0\" 200 36440 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n23.94.80.209 - - [27/Oct/2015:04:47:01 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n23.94.80.209 - - [27/Oct/2015:04:47:02 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n23.94.80.209 - - [27/Oct/2015:04:47:04 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n23.94.80.209 - - [27/Oct/2015:04:47:05 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n23.94.80.209 - - [27/Oct/2015:04:47:06 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n31.220.113.224 - - [27/Oct/2015:04:51:34 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.0\" 200 24319 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n31.220.113.224 - - [27/Oct/2015:04:51:35 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n31.220.113.224 - - [27/Oct/2015:04:51:35 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n31.220.113.224 - - [27/Oct/2015:04:51:36 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n31.220.113.224 - - [27/Oct/2015:04:51:36 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n31.220.113.224 - - [27/Oct/2015:04:51:37 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n192.99.244.139 - - [27/Oct/2015:05:06:32 +0100] \"GET /accessibility-info HTTP/1.1\" 200 20622 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n192.99.244.139 - - [27/Oct/2015:05:06:33 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n192.99.244.139 - - [27/Oct/2015:05:06:34 +0100] \"POST /join_form HTTP/1.1\" 302 10322 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n192.99.244.139 - - [27/Oct/2015:05:06:35 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n192.99.244.139 - - [27/Oct/2015:05:06:36 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n192.99.244.139 - - [27/Oct/2015:05:06:36 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n192.3.193.93 - - [27/Oct/2015:05:28:18 +0100] \"GET /places HTTP/1.0\" 200 19355 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n192.3.193.93 - - [27/Oct/2015:05:28:20 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n192.3.193.93 - - [27/Oct/2015:05:28:21 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n192.3.193.93 - - [27/Oct/2015:05:28:22 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n107.172.9.148 - - [27/Oct/2015:05:28:23 +0100] \"GET /login_form HTTP/1.0\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n107.172.9.148 - - [27/Oct/2015:05:28:24 +0100] \"GET /search?SearchableText= HTTP/1.1\" 200 10773 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n107.172.9.148 - - [27/Oct/2015:05:28:26 +0100] \"GET /login_form HTTP/1.1\" 200 11642 \"http://niels.basjes.nl/search?SearchableText=\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n107.172.9.148 - - [27/Oct/2015:05:28:27 +0100] \"POST /login_form HTTP/1.1\" 200 18372 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n31.220.113.224 - - [27/Oct/2015:05:32:31 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.0\" 200 24319 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n31.220.113.224 - - [27/Oct/2015:05:32:32 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n31.220.113.224 - - [27/Oct/2015:05:32:33 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n31.220.113.224 - - [27/Oct/2015:05:32:33 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n31.220.113.224 - - [27/Oct/2015:05:32:34 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n31.220.113.224 - - [27/Oct/2015:05:32:34 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n188.210.215.223 - - [27/Oct/2015:05:37:24 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.0\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n188.210.215.223 - - [27/Oct/2015:05:37:25 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n188.210.215.223 - - [27/Oct/2015:05:37:25 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n165.231.27.136 - - [27/Oct/2015:05:46:23 +0100] \"GET /linux/installing-gitlab-on-centos-6 HTTP/1.0\" 200 19418 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n165.231.27.136 - - [27/Oct/2015:05:46:24 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n165.231.27.136 - - [27/Oct/2015:05:46:25 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n165.231.27.136 - - [27/Oct/2015:05:46:26 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n165.231.27.136 - - [27/Oct/2015:05:46:27 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n165.231.27.136 - - [27/Oct/2015:05:46:28 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n180.180.109.46 - - [27/Oct/2015:05:48:07 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.1\" 200 24319 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n180.180.109.46 - - [27/Oct/2015:05:48:10 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n180.180.109.46 - - [27/Oct/2015:05:48:12 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n180.180.109.46 - - [27/Oct/2015:05:48:14 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n180.180.109.46 - - [27/Oct/2015:05:48:19 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n180.180.109.46 - - [27/Oct/2015:05:48:20 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n155.254.119.236 - - [27/Oct/2015:05:57:18 +0100] \"GET /join_form HTTP/1.0\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n155.254.119.236 - - [27/Oct/2015:05:57:20 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n155.254.119.236 - - [27/Oct/2015:05:57:20 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n155.254.119.236 - - [27/Oct/2015:05:57:21 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n155.254.119.236 - - [27/Oct/2015:05:57:22 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n50.31.9.98 - - [27/Oct/2015:06:02:45 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.0\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n50.31.9.98 - - [27/Oct/2015:06:02:48 +0100] \"GET /join_form HTTP/1.0\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n170.130.59.239 - - [27/Oct/2015:06:02:54 +0100] \"POST /join_form HTTP/1.0\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n170.130.59.239 - - [27/Oct/2015:06:03:06 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.0\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n199.195.158.63 - - [27/Oct/2015:06:23:42 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n199.195.158.63 - - [27/Oct/2015:06:23:43 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n180.180.99.74 - - [27/Oct/2015:06:27:02 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.1\" 200 24319 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n180.180.99.74 - - [27/Oct/2015:06:27:04 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n180.180.99.74 - - [27/Oct/2015:06:27:05 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n180.180.99.74 - - [27/Oct/2015:06:27:07 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n180.180.99.74 - - [27/Oct/2015:06:27:08 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n180.180.99.74 - - [27/Oct/2015:06:27:10 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n198.12.66.139 - - [27/Oct/2015:06:32:38 +0100] \"GET /linux/installing-my-new-server/vmware-server HTTP/1.0\" 200 25005 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n198.12.66.139 - - [27/Oct/2015:06:32:40 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n198.12.66.139 - - [27/Oct/2015:06:32:41 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n198.12.66.139 - - [27/Oct/2015:06:32:47 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n198.12.66.139 - - [27/Oct/2015:06:32:48 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n198.12.66.139 - - [27/Oct/2015:06:32:54 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n104.144.19.69 - - [27/Oct/2015:06:33:52 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.0\" 200 24319 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n104.144.19.69 - - [27/Oct/2015:06:33:54 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n104.144.19.69 - - [27/Oct/2015:06:33:56 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n104.144.19.69 - - [27/Oct/2015:06:33:58 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n104.144.19.69 - - [27/Oct/2015:06:33:59 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n104.144.19.69 - - [27/Oct/2015:06:34:01 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n192.99.244.139 - - [27/Oct/2015:06:36:55 +0100] \"GET /accessibility-info HTTP/1.1\" 200 20622 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n158.222.15.195 - - [27/Oct/2015:06:39:17 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n158.222.15.195 - - [27/Oct/2015:06:39:18 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n158.222.15.195 - - [27/Oct/2015:06:39:19 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n158.222.15.195 - - [27/Oct/2015:06:39:20 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n158.222.15.195 - - [27/Oct/2015:06:39:21 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n158.222.15.195 - - [27/Oct/2015:06:39:22 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n61.75.2.124 - - [27/Oct/2015:06:48:48 +0100] \"GET / HTTP/1.0\" 200 17053 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n61.75.2.124 - - [27/Oct/2015:06:49:04 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n61.75.2.124 - - [27/Oct/2015:06:49:17 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n61.75.2.124 - - [27/Oct/2015:06:49:29 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n61.75.2.124 - - [27/Oct/2015:06:49:42 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n61.75.2.124 - - [27/Oct/2015:06:49:54 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n155.254.119.252 - - [27/Oct/2015:06:50:32 +0100] \"GET /join_form HTTP/1.0\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n155.254.119.252 - - [27/Oct/2015:06:50:33 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n155.254.119.252 - - [27/Oct/2015:06:50:34 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n155.254.119.252 - - [27/Oct/2015:06:50:35 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n155.254.119.252 - - [27/Oct/2015:06:50:36 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n198.23.158.188 - - [27/Oct/2015:06:52:11 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n198.23.158.188 - - [27/Oct/2015:06:52:12 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n23.247.182.225 - - [27/Oct/2015:07:14:26 +0100] \"GET / HTTP/1.0\" 200 20474 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n23.247.182.225 - - [27/Oct/2015:07:14:27 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n23.247.182.225 - - [27/Oct/2015:07:14:28 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n23.247.182.225 - - [27/Oct/2015:07:14:29 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n23.247.182.225 - - [27/Oct/2015:07:14:30 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n23.247.182.225 - - [27/Oct/2015:07:14:30 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n198.23.158.188 - - [27/Oct/2015:07:19:25 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n198.23.158.188 - - [27/Oct/2015:07:19:26 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n198.23.158.188 - - [27/Oct/2015:07:19:27 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n198.23.158.188 - - [27/Oct/2015:07:19:27 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n180.180.103.125 - - [27/Oct/2015:07:19:40 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.1\" 200 24319 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n180.180.103.125 - - [27/Oct/2015:07:19:42 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n180.180.103.125 - - [27/Oct/2015:07:19:43 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n180.180.103.125 - - [27/Oct/2015:07:19:44 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n180.180.103.125 - - [27/Oct/2015:07:19:46 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n180.180.103.125 - - [27/Oct/2015:07:19:47 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n104.254.212.109 - - [27/Oct/2015:07:40:07 +0100] \"GET /splittable-gzip HTTP/1.1\" 200 27869 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n104.254.212.109 - - [27/Oct/2015:07:40:09 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n104.254.212.109 - - [27/Oct/2015:07:40:10 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n104.254.212.109 - - [27/Oct/2015:07:40:11 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n104.254.212.109 - - [27/Oct/2015:07:40:12 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n104.254.212.109 - - [27/Oct/2015:07:40:13 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0\"\n50.31.9.73 - - [27/Oct/2015:08:04:46 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.0\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n50.31.9.215 - - [27/Oct/2015:08:04:47 +0100] \"GET /login_form HTTP/1.0\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n50.31.9.73 - - [27/Oct/2015:08:04:49 +0100] \"POST /login_form HTTP/1.0\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n187.87.166.63 - - [27/Oct/2015:08:14:59 +0100] \"GET /places HTTP/1.1\" 200 19355 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n187.87.166.63 - - [27/Oct/2015:08:15:18 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n14.72.24.3 - - [27/Oct/2015:08:15:37 +0100] \"POST /join_form HTTP/1.1\" 302 10322 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n14.72.24.3 - - [27/Oct/2015:08:15:40 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n14.72.24.3 - - [27/Oct/2015:08:15:42 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n14.72.24.3 - - [27/Oct/2015:08:15:44 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n23.247.140.245 - - [27/Oct/2015:08:30:05 +0100] \"GET /join_form HTTP/1.0\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n107.168.10.150 - - [27/Oct/2015:08:30:07 +0100] \"POST /join_form HTTP/1.0\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n107.168.10.150 - - [27/Oct/2015:08:30:09 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.0\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n107.168.10.150 - - [27/Oct/2015:08:30:10 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n107.168.10.150 - - [27/Oct/2015:08:30:13 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n104.144.19.69 - - [27/Oct/2015:08:34:02 +0100] \"GET / HTTP/1.0\" 200 20474 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n104.144.19.69 - - [27/Oct/2015:08:34:04 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n104.144.19.69 - - [27/Oct/2015:08:34:05 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n104.144.19.69 - - [27/Oct/2015:08:34:07 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n104.144.19.69 - - [27/Oct/2015:08:34:10 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n104.144.19.69 - - [27/Oct/2015:08:34:11 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n198.12.82.177 - - [27/Oct/2015:08:47:11 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36440 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (X11; CrOS x86_64 6310.68.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.96 Safari/537.36\"\n198.12.82.177 - - [27/Oct/2015:08:47:13 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (X11; CrOS x86_64 6310.68.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.96 Safari/537.36\"\n198.12.82.177 - - [27/Oct/2015:08:47:15 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (X11; CrOS x86_64 6310.68.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.96 Safari/537.36\"\n198.12.82.177 - - [27/Oct/2015:08:47:16 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (X11; CrOS x86_64 6310.68.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.96 Safari/537.36\"\n198.12.82.177 - - [27/Oct/2015:08:47:17 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (X11; CrOS x86_64 6310.68.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.96 Safari/537.36\"\n198.12.82.177 - - [27/Oct/2015:08:47:18 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (X11; CrOS x86_64 6310.68.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.96 Safari/537.36\"\n216.158.196.24 - - [27/Oct/2015:08:52:55 +0100] \"GET /open-source HTTP/1.0\" 200 17525 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n216.158.196.24 - - [27/Oct/2015:08:53:04 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n216.158.196.24 - - [27/Oct/2015:08:53:05 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n216.158.196.24 - - [27/Oct/2015:08:53:07 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n158.222.8.215 - - [27/Oct/2015:08:56:20 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.0\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n158.222.8.215 - - [27/Oct/2015:08:56:22 +0100] \"GET /login_form HTTP/1.0\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n216.158.197.69 - - [27/Oct/2015:08:56:24 +0100] \"POST /login_form HTTP/1.0\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n37.203.212.82 - - [27/Oct/2015:09:00:03 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.0\" 200 24319 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n37.203.212.82 - - [27/Oct/2015:09:00:03 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n37.203.212.82 - - [27/Oct/2015:09:00:05 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n37.203.212.82 - - [27/Oct/2015:09:00:06 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n23.106.192.53 - - [27/Oct/2015:09:18:28 +0100] \"GET /join_form HTTP/1.0\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n23.106.192.53 - - [27/Oct/2015:09:18:30 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n23.106.192.53 - - [27/Oct/2015:09:18:32 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n23.106.192.53 - - [27/Oct/2015:09:18:34 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n23.106.192.53 - - [27/Oct/2015:09:18:36 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n192.210.223.92 - - [27/Oct/2015:09:21:04 +0100] \"GET / HTTP/1.1\" 200 20474 \"http://niels.basjes.nl\" \"Mozilla/5.0 (X11; CrOS x86_64 6310.68.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.96 Safari/537.36\"\n192.210.223.92 - - [27/Oct/2015:09:21:06 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl\" \"Mozilla/5.0 (X11; CrOS x86_64 6310.68.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.96 Safari/537.36\"\n192.210.223.92 - - [27/Oct/2015:09:21:08 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (X11; CrOS x86_64 6310.68.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.96 Safari/537.36\"\n192.210.223.92 - - [27/Oct/2015:09:21:08 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (X11; CrOS x86_64 6310.68.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.96 Safari/537.36\"\n192.210.223.92 - - [27/Oct/2015:09:21:10 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (X11; CrOS x86_64 6310.68.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.96 Safari/537.36\"\n192.210.223.92 - - [27/Oct/2015:09:21:11 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (X11; CrOS x86_64 6310.68.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.96 Safari/537.36\"\n5.157.10.116 - - [27/Oct/2015:09:41:03 +0100] \"GET /linux/installing-gitlab-on-centos-6 HTTP/1.0\" 200 19418 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n5.157.10.116 - - [27/Oct/2015:09:41:04 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n5.157.10.116 - - [27/Oct/2015:09:41:05 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n5.157.10.116 - - [27/Oct/2015:09:41:06 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n5.157.10.116 - - [27/Oct/2015:09:41:06 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n5.157.10.116 - - [27/Oct/2015:09:41:07 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n23.94.80.152 - - [27/Oct/2015:09:45:59 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.0\" 200 36440 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n23.94.80.152 - - [27/Oct/2015:09:46:01 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n23.94.80.152 - - [27/Oct/2015:09:46:02 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n23.94.80.152 - - [27/Oct/2015:09:46:03 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n23.94.80.152 - - [27/Oct/2015:09:46:04 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n23.94.80.152 - - [27/Oct/2015:09:46:06 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n107.161.83.180 - - [27/Oct/2015:09:46:29 +0100] \"GET / HTTP/1.1\" 200 20474 \"http://niels.basjes.nl\" \"Mozilla/5.0 (X11; CrOS x86_64 6310.68.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.96 Safari/537.36\"\n107.161.83.180 - - [27/Oct/2015:09:46:31 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl\" \"Mozilla/5.0 (X11; CrOS x86_64 6310.68.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.96 Safari/537.36\"\n107.161.83.180 - - [27/Oct/2015:09:46:33 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (X11; CrOS x86_64 6310.68.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.96 Safari/537.36\"\n107.161.83.180 - - [27/Oct/2015:09:46:34 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (X11; CrOS x86_64 6310.68.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.96 Safari/537.36\"\n107.161.83.180 - - [27/Oct/2015:09:46:35 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (X11; CrOS x86_64 6310.68.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.96 Safari/537.36\"\n107.161.83.180 - - [27/Oct/2015:09:46:36 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (X11; CrOS x86_64 6310.68.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.96 Safari/537.36\"\n192.161.195.201 - - [27/Oct/2015:09:55:06 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n192.161.195.201 - - [27/Oct/2015:09:55:07 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n192.161.195.201 - - [27/Oct/2015:09:55:08 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n172.245.119.26 - - [27/Oct/2015:09:55:51 +0100] \"GET /join_form HTTP/1.0\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n172.245.119.26 - - [27/Oct/2015:09:55:52 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n172.245.119.26 - - [27/Oct/2015:09:55:53 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n172.245.119.26 - - [27/Oct/2015:09:55:54 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n172.245.119.26 - - [27/Oct/2015:09:55:56 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n192.126.192.60 - - [27/Oct/2015:10:16:11 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.0\" 200 24319 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n192.126.192.60 - - [27/Oct/2015:10:16:13 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n192.126.192.60 - - [27/Oct/2015:10:16:14 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n192.126.192.60 - - [27/Oct/2015:10:16:16 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n192.126.192.60 - - [27/Oct/2015:10:16:17 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n192.126.192.60 - - [27/Oct/2015:10:16:19 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n23.254.164.173 - - [27/Oct/2015:10:16:56 +0100] \"GET /linux/installing-my-new-server/Various-software/RK=0/RS=C_oF6PJOAKvJ3JsgiwAbhNKI2uE- HTTP/1.1\" 404 12145 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.110 Safari/537.36\"\n23.254.164.173 - - [27/Oct/2015:10:16:57 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/linux/installing-my-new-server/Various-software/RK=0/RS=C_oF6PJOAKvJ3JsgiwAbhNKI2uE-\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.110 Safari/537.36\"\n23.254.164.173 - - [27/Oct/2015:10:16:57 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.110 Safari/537.36\"\n23.254.164.173 - - [27/Oct/2015:10:16:58 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.110 Safari/537.36\"\n23.254.164.173 - - [27/Oct/2015:10:16:59 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.110 Safari/537.36\"\n23.254.164.173 - - [27/Oct/2015:10:17:00 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.110 Safari/537.36\"\n23.95.236.173 - - [27/Oct/2015:10:27:49 +0100] \"GET /places HTTP/1.0\" 200 19355 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n23.95.236.173 - - [27/Oct/2015:10:27:51 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n23.95.236.173 - - [27/Oct/2015:10:27:53 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n23.95.236.173 - - [27/Oct/2015:10:27:55 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n23.95.236.173 - - [27/Oct/2015:10:27:58 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n23.95.236.173 - - [27/Oct/2015:10:28:00 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n23.94.177.173 - - [27/Oct/2015:10:57:28 +0100] \"GET /linux/installing-my-new-server/vmware-server HTTP/1.0\" 200 25005 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n23.94.177.173 - - [27/Oct/2015:10:57:30 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n23.94.177.173 - - [27/Oct/2015:10:57:30 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n23.94.177.173 - - [27/Oct/2015:10:57:31 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n23.94.177.173 - - [27/Oct/2015:10:57:32 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n23.94.177.173 - - [27/Oct/2015:10:57:33 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n94.242.246.238 - - [27/Oct/2015:11:00:23 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.0\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n94.242.246.238 - - [27/Oct/2015:11:00:24 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n94.242.246.238 - - [27/Oct/2015:11:00:24 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n192.161.171.34 - - [27/Oct/2015:11:13:41 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//facebookhacktips.weebly.com HTTP/1.0\" 200 10664 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n192.161.171.34 - - [27/Oct/2015:11:13:43 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/acl_users/credentials_cookie_auth/require_login?came_from=http%3A//facebookhacktips.weebly.com\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n192.161.171.34 - - [27/Oct/2015:11:13:45 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n192.255.77.234 - - [27/Oct/2015:11:25:09 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.0\" 200 36440 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n192.255.77.234 - - [27/Oct/2015:11:25:10 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n192.255.77.234 - - [27/Oct/2015:11:25:12 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n192.255.77.234 - - [27/Oct/2015:11:25:13 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n192.255.77.234 - - [27/Oct/2015:11:25:15 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n192.255.77.234 - - [27/Oct/2015:11:25:16 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n89.36.66.187 - - [27/Oct/2015:11:42:47 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.0\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n89.36.66.187 - - [27/Oct/2015:11:42:48 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n23.247.182.75 - - [27/Oct/2015:11:59:12 +0100] \"GET / HTTP/1.0\" 200 20474 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n23.247.182.75 - - [27/Oct/2015:11:59:13 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n23.247.182.75 - - [27/Oct/2015:11:59:14 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n148.251.65.34 - - [27/Oct/2015:12:20:59 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36440 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n192.119.144.246 - - [27/Oct/2015:12:21:01 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n192.119.144.246 - - [27/Oct/2015:12:21:02 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n192.119.144.246 - - [27/Oct/2015:12:21:03 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n192.119.144.246 - - [27/Oct/2015:12:21:04 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n192.119.144.246 - - [27/Oct/2015:12:21:06 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n148.251.65.34 - - [27/Oct/2015:12:22:53 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36440 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n192.119.144.246 - - [27/Oct/2015:12:22:55 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n192.119.144.246 - - [27/Oct/2015:12:22:56 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n192.119.144.246 - - [27/Oct/2015:12:22:57 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n192.119.144.246 - - [27/Oct/2015:12:22:58 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n192.119.144.246 - - [27/Oct/2015:12:22:59 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n216.158.211.139 - - [27/Oct/2015:12:26:59 +0100] \"GET /open-source HTTP/1.0\" 200 17525 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n216.158.211.139 - - [27/Oct/2015:12:27:01 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n216.158.211.139 - - [27/Oct/2015:12:27:03 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n216.158.211.139 - - [27/Oct/2015:12:27:05 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n216.158.200.122 - - [27/Oct/2015:12:30:57 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.0\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n216.158.200.122 - - [27/Oct/2015:12:30:58 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n216.158.200.122 - - [27/Oct/2015:12:31:01 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n178.216.54.249 - - [27/Oct/2015:13:00:58 +0100] \"GET /join_form HTTP/1.0\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n178.216.54.249 - - [27/Oct/2015:13:00:59 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n178.216.54.249 - - [27/Oct/2015:13:01:01 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n178.216.54.249 - - [27/Oct/2015:13:01:02 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n178.216.54.249 - - [27/Oct/2015:13:01:03 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n94.242.246.233 - - [27/Oct/2015:13:02:19 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n94.242.246.233 - - [27/Oct/2015:13:02:19 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n170.130.59.239 - - [27/Oct/2015:13:07:58 +0100] \"GET /places HTTP/1.0\" 200 19355 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n170.130.59.239 - - [27/Oct/2015:13:08:16 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n170.130.59.239 - - [27/Oct/2015:13:08:27 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n170.130.59.239 - - [27/Oct/2015:13:08:38 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n23.95.181.217 - - [27/Oct/2015:13:12:02 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n23.95.181.217 - - [27/Oct/2015:13:12:03 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n23.95.181.217 - - [27/Oct/2015:13:12:05 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n23.95.181.217 - - [27/Oct/2015:13:12:06 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n23.95.181.217 - - [27/Oct/2015:13:12:07 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n23.254.164.173 - - [27/Oct/2015:13:17:33 +0100] \"GET /linux/installing-my-new-server/voice-over-ip/RK=0/RS=YTkYTnGaf5QQxT7cm9uJgKS2rl8- HTTP/1.1\" 404 12146 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.110 Safari/537.36\"\n23.254.164.173 - - [27/Oct/2015:13:17:34 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/linux/installing-my-new-server/voice-over-ip/RK=0/RS=YTkYTnGaf5QQxT7cm9uJgKS2rl8-\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.110 Safari/537.36\"\n23.254.164.173 - - [27/Oct/2015:13:17:35 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.110 Safari/537.36\"\n23.254.164.173 - - [27/Oct/2015:13:17:36 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.110 Safari/537.36\"\n23.254.164.173 - - [27/Oct/2015:13:17:37 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.110 Safari/537.36\"\n23.254.164.173 - - [27/Oct/2015:13:17:38 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.110 Safari/537.36\"\n192.210.223.69 - - [27/Oct/2015:13:22:55 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36440 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (X11; CrOS x86_64 6310.68.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.96 Safari/537.36\"\n192.210.223.69 - - [27/Oct/2015:13:22:58 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (X11; CrOS x86_64 6310.68.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.96 Safari/537.36\"\n192.210.223.69 - - [27/Oct/2015:13:22:59 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (X11; CrOS x86_64 6310.68.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.96 Safari/537.36\"\n192.210.223.69 - - [27/Oct/2015:13:23:00 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (X11; CrOS x86_64 6310.68.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.96 Safari/537.36\"\n192.210.223.69 - - [27/Oct/2015:13:23:01 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (X11; CrOS x86_64 6310.68.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.96 Safari/537.36\"\n192.210.223.69 - - [27/Oct/2015:13:23:02 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (X11; CrOS x86_64 6310.68.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.96 Safari/537.36\"\n50.2.190.172 - - [27/Oct/2015:14:12:02 +0100] \"GET /splittable-gzip HTTP/1.0\" 200 27760 \"http://niels.basj.es/\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/600.2.5 (KHTML, like Gecko) Version/8.0.2 Safari/600.2.5\"\n50.2.190.172 - - [27/Oct/2015:14:12:04 +0100] \"GET /join_form HTTP/1.1\" 200 12121 \"http://niels.basj.es/\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/600.2.5 (KHTML, like Gecko) Version/8.0.2 Safari/600.2.5\"\n50.2.190.172 - - [27/Oct/2015:14:12:05 +0100] \"POST /join_form HTTP/1.1\" 302 10098 \"http://niels.basj.es/join_form\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/600.2.5 (KHTML, like Gecko) Version/8.0.2 Safari/600.2.5\"\n50.2.190.172 - - [27/Oct/2015:14:12:07 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basj.es/join_form HTTP/1.1\" 200 11713 \"http://niels.basj.es/join_form\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/600.2.5 (KHTML, like Gecko) Version/8.0.2 Safari/600.2.5\"\n50.2.190.172 - - [27/Oct/2015:14:12:08 +0100] \"GET /login_form HTTP/1.1\" 200 11544 \"http://niels.basj.es/\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/600.2.5 (KHTML, like Gecko) Version/8.0.2 Safari/600.2.5\"\n50.2.190.172 - - [27/Oct/2015:14:12:10 +0100] \"POST /login_form HTTP/1.1\" 200 18237 \"http://niels.basj.es/login_form\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/600.2.5 (KHTML, like Gecko) Version/8.0.2 Safari/600.2.5\"\n50.31.9.98 - - [27/Oct/2015:14:26:24 +0100] \"GET /places HTTP/1.0\" 200 19355 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n50.31.9.73 - - [27/Oct/2015:14:26:28 +0100] \"GET /join_form HTTP/1.0\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n50.31.9.98 - - [27/Oct/2015:14:26:32 +0100] \"POST /join_form HTTP/1.0\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n198.144.182.240 - - [27/Oct/2015:14:26:58 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//socialnetworksafety.edublogs.org HTTP/1.0\" 200 10674 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n198.144.182.240 - - [27/Oct/2015:14:27:00 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/acl_users/credentials_cookie_auth/require_login?came_from=http%3A//socialnetworksafety.edublogs.org\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n198.144.182.240 - - [27/Oct/2015:14:27:02 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n216.158.211.172 - - [27/Oct/2015:15:04:19 +0100] \"GET /linux/installing-my-new-server/networking HTTP/1.0\" 200 27448 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n216.158.211.172 - - [27/Oct/2015:15:04:24 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n216.158.211.172 - - [27/Oct/2015:15:04:26 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n216.158.211.172 - - [27/Oct/2015:15:04:29 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n155.94.140.99 - - [27/Oct/2015:15:04:30 +0100] \"GET /login_form HTTP/1.0\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n155.94.140.99 - - [27/Oct/2015:15:04:39 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n89.32.251.222 - - [27/Oct/2015:15:19:02 +0100] \"GET /join_form HTTP/1.0\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n89.32.251.222 - - [27/Oct/2015:15:19:03 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n89.32.251.222 - - [27/Oct/2015:15:19:04 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n89.32.251.222 - - [27/Oct/2015:15:19:05 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n89.32.251.222 - - [27/Oct/2015:15:19:06 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n196.196.20.88 - - [27/Oct/2015:15:21:23 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n196.196.20.88 - - [27/Oct/2015:15:21:24 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n196.196.20.88 - - [27/Oct/2015:15:21:25 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n198.12.117.183 - - [27/Oct/2015:15:21:26 +0100] \"GET /login_form HTTP/1.0\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n198.12.117.183 - - [27/Oct/2015:15:21:27 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n198.12.117.183 - - [27/Oct/2015:15:24:03 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n198.12.117.183 - - [27/Oct/2015:15:24:04 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n198.12.117.183 - - [27/Oct/2015:15:24:06 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n198.12.117.183 - - [27/Oct/2015:15:24:07 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n198.12.117.183 - - [27/Oct/2015:15:24:09 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n23.254.164.173 - - [27/Oct/2015:15:24:28 +0100] \"GET /linux/installing-my-new-server/voice-over-ip/RK=0/RS=YTkYTnGaf5QQxT7cm9uJgKS2rl8- HTTP/1.1\" 404 12146 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.110 Safari/537.36\"\n23.254.164.173 - - [27/Oct/2015:15:24:29 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/linux/installing-my-new-server/voice-over-ip/RK=0/RS=YTkYTnGaf5QQxT7cm9uJgKS2rl8-\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.110 Safari/537.36\"\n23.254.164.173 - - [27/Oct/2015:15:24:30 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.110 Safari/537.36\"\n23.254.164.173 - - [27/Oct/2015:15:24:31 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.110 Safari/537.36\"\n23.254.164.173 - - [27/Oct/2015:15:24:33 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.110 Safari/537.36\"\n23.254.164.173 - - [27/Oct/2015:15:24:34 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.110 Safari/537.36\"\n162.253.53.42 - - [27/Oct/2015:15:31:35 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36440 \"-\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n162.253.53.42 - - [27/Oct/2015:15:31:39 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"-\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n162.253.53.42 - - [27/Oct/2015:15:31:39 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"-\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n162.253.53.42 - - [27/Oct/2015:15:31:40 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"-\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n162.253.53.42 - - [27/Oct/2015:15:31:41 +0100] \"GET /login_form HTTP/1.1\" 200 10505 \"-\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n162.253.53.42 - - [27/Oct/2015:15:31:41 +0100] \"POST /login_form HTTP/1.1\" 200 16768 \"-\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n31.187.79.152 - - [27/Oct/2015:15:47:35 +0100] \"GET /author/nielsbasjes HTTP/1.0\" 200 15519 \"http://niels.basj.es/\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/600.2.5 (KHTML, like Gecko) Version/8.0.2 Safari/600.2.5\"\n31.187.79.152 - - [27/Oct/2015:15:47:35 +0100] \"GET /join_form HTTP/1.1\" 200 12121 \"http://niels.basj.es/\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/600.2.5 (KHTML, like Gecko) Version/8.0.2 Safari/600.2.5\"\n31.187.79.152 - - [27/Oct/2015:15:47:36 +0100] \"POST /join_form HTTP/1.1\" 302 10098 \"http://niels.basj.es/join_form\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/600.2.5 (KHTML, like Gecko) Version/8.0.2 Safari/600.2.5\"\n31.187.79.152 - - [27/Oct/2015:15:47:36 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basj.es/join_form HTTP/1.1\" 200 11713 \"http://niels.basj.es/join_form\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/600.2.5 (KHTML, like Gecko) Version/8.0.2 Safari/600.2.5\"\n31.187.79.152 - - [27/Oct/2015:15:47:37 +0100] \"GET /login_form HTTP/1.1\" 200 11544 \"http://niels.basj.es/\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/600.2.5 (KHTML, like Gecko) Version/8.0.2 Safari/600.2.5\"\n31.187.79.152 - - [27/Oct/2015:15:47:38 +0100] \"POST /login_form HTTP/1.1\" 200 18237 \"http://niels.basj.es/login_form\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/600.2.5 (KHTML, like Gecko) Version/8.0.2 Safari/600.2.5\"\n69.12.79.119 - - [27/Oct/2015:15:55:49 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n69.12.79.119 - - [27/Oct/2015:15:55:50 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n69.12.79.119 - - [27/Oct/2015:15:55:51 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n69.12.79.119 - - [27/Oct/2015:15:55:53 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n69.12.79.119 - - [27/Oct/2015:15:55:55 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n192.161.239.142 - - [27/Oct/2015:15:56:30 +0100] \"GET / HTTP/1.0\" 200 20474 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n192.161.239.142 - - [27/Oct/2015:15:56:31 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n192.161.239.142 - - [27/Oct/2015:15:56:32 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n192.161.239.142 - - [27/Oct/2015:15:56:33 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n192.161.239.142 - - [27/Oct/2015:15:56:34 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n192.161.239.142 - - [27/Oct/2015:15:56:35 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n94.242.212.19 - - [27/Oct/2015:16:10:56 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basj.es/join_form HTTP/1.0\" 200 11713 \"http://niels.basj.es/\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/600.2.5 (KHTML, like Gecko) Version/8.0.2 Safari/600.2.5\"\n94.242.212.19 - - [27/Oct/2015:16:10:57 +0100] \"GET /join_form HTTP/1.1\" 200 12121 \"http://niels.basj.es/\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/600.2.5 (KHTML, like Gecko) Version/8.0.2 Safari/600.2.5\"\n94.242.212.19 - - [27/Oct/2015:16:11:01 +0100] \"POST /join_form HTTP/1.1\" 302 10098 \"http://niels.basj.es/join_form\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/600.2.5 (KHTML, like Gecko) Version/8.0.2 Safari/600.2.5\"\n94.242.212.19 - - [27/Oct/2015:16:11:02 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basj.es/join_form HTTP/1.1\" 200 11713 \"http://niels.basj.es/join_form\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/600.2.5 (KHTML, like Gecko) Version/8.0.2 Safari/600.2.5\"\n94.242.212.19 - - [27/Oct/2015:16:11:02 +0100] \"GET /login_form HTTP/1.1\" 200 11544 \"http://niels.basj.es/\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/600.2.5 (KHTML, like Gecko) Version/8.0.2 Safari/600.2.5\"\n94.242.212.19 - - [27/Oct/2015:16:11:03 +0100] \"POST /login_form HTTP/1.1\" 200 18237 \"http://niels.basj.es/login_form\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/600.2.5 (KHTML, like Gecko) Version/8.0.2 Safari/600.2.5\"\n155.94.168.77 - - [27/Oct/2015:16:11:36 +0100] \"GET /join_form HTTP/1.0\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n155.94.168.77 - - [27/Oct/2015:16:12:02 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n155.94.168.77 - - [27/Oct/2015:16:12:12 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n155.94.168.77 - - [27/Oct/2015:16:12:28 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n155.94.168.77 - - [27/Oct/2015:16:12:41 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n185.75.57.58 - - [27/Oct/2015:16:24:19 +0100] \"GET /places HTTP/1.0\" 200 19355 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n185.75.57.58 - - [27/Oct/2015:16:24:20 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n185.75.57.58 - - [27/Oct/2015:16:24:22 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n185.75.57.58 - - [27/Oct/2015:16:24:23 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n185.75.57.58 - - [27/Oct/2015:16:24:25 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n185.75.57.58 - - [27/Oct/2015:16:24:26 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n50.31.9.215 - - [27/Oct/2015:16:40:04 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.0\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n50.31.9.73 - - [27/Oct/2015:16:40:07 +0100] \"GET /login_form HTTP/1.0\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n69.163.47.119 - - [27/Oct/2015:16:40:55 +0100] \"GET /places HTTP/1.1\" 200 19355 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n190.63.175.247 - - [27/Oct/2015:16:41:01 +0100] \"GET /join_form HTTP/1.0\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n218.200.66.196 - - [27/Oct/2015:16:41:37 +0100] \"POST /join_form HTTP/1.0\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n218.200.66.196 - - [27/Oct/2015:16:41:41 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.0\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n218.200.66.196 - - [27/Oct/2015:16:41:45 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n218.200.66.196 - - [27/Oct/2015:16:41:51 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n5.157.42.183 - - [27/Oct/2015:16:51:06 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.0\" 200 24319 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n5.157.42.183 - - [27/Oct/2015:16:51:07 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n5.157.42.183 - - [27/Oct/2015:16:51:07 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n5.157.42.183 - - [27/Oct/2015:16:51:08 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n5.157.42.183 - - [27/Oct/2015:16:51:08 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n5.157.42.183 - - [27/Oct/2015:16:51:09 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n192.161.239.178 - - [27/Oct/2015:16:57:12 +0100] \"GET /linux/installing-my-new-server/networking HTTP/1.0\" 200 27448 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n192.161.239.178 - - [27/Oct/2015:16:57:13 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n192.161.239.178 - - [27/Oct/2015:16:57:14 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n192.161.239.178 - - [27/Oct/2015:16:57:15 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n192.161.239.178 - - [27/Oct/2015:16:57:16 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n192.161.239.178 - - [27/Oct/2015:16:57:17 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n107.158.89.173 - - [27/Oct/2015:17:09:34 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n107.158.89.173 - - [27/Oct/2015:17:09:35 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n107.158.89.173 - - [27/Oct/2015:17:09:36 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n107.158.89.173 - - [27/Oct/2015:17:09:38 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n107.158.89.173 - - [27/Oct/2015:17:09:39 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n158.222.12.39 - - [27/Oct/2015:17:30:16 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36440 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (X11; CrOS x86_64 6310.68.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.96 Safari/537.36\"\n158.222.12.39 - - [27/Oct/2015:17:30:18 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (X11; CrOS x86_64 6310.68.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.96 Safari/537.36\"\n158.222.12.39 - - [27/Oct/2015:17:30:20 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (X11; CrOS x86_64 6310.68.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.96 Safari/537.36\"\n158.222.12.39 - - [27/Oct/2015:17:30:21 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (X11; CrOS x86_64 6310.68.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.96 Safari/537.36\"\n158.222.12.39 - - [27/Oct/2015:17:30:22 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (X11; CrOS x86_64 6310.68.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.96 Safari/537.36\"\n158.222.12.39 - - [27/Oct/2015:17:30:23 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (X11; CrOS x86_64 6310.68.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.96 Safari/537.36\"\n104.227.66.81 - - [27/Oct/2015:17:30:58 +0100] \"GET /linux/installing-my-new-server/networking HTTP/1.0\" 200 27448 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n104.227.66.81 - - [27/Oct/2015:17:31:00 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n104.227.66.81 - - [27/Oct/2015:17:31:01 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n104.227.66.81 - - [27/Oct/2015:17:31:02 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n104.227.66.81 - - [27/Oct/2015:17:31:03 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n104.227.66.81 - - [27/Oct/2015:17:31:04 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n173.232.116.137 - - [27/Oct/2015:17:59:28 +0100] \"GET /join_form HTTP/1.0\" 200 11652 \"http://daniel_en_sander.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n173.232.116.137 - - [27/Oct/2015:17:59:30 +0100] \"POST /join_form HTTP/1.1\" 302 9556 \"http://daniel_en_sander.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n173.232.116.137 - - [27/Oct/2015:17:59:36 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//daniel_en_sander.basjes.nl/join_form HTTP/1.1\" 403 340 \"http://daniel_en_sander.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n94.242.208.187 - - [27/Oct/2015:18:05:29 +0100] \"GET /sitemap HTTP/1.0\" 200 11975 \"http://niels.basj.es/\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/600.2.5 (KHTML, like Gecko) Version/8.0.2 Safari/600.2.5\"\n94.242.208.187 - - [27/Oct/2015:18:05:31 +0100] \"GET /join_form HTTP/1.1\" 200 12121 \"http://niels.basj.es/\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/600.2.5 (KHTML, like Gecko) Version/8.0.2 Safari/600.2.5\"\n94.242.208.187 - - [27/Oct/2015:18:05:31 +0100] \"POST /join_form HTTP/1.1\" 302 10098 \"http://niels.basj.es/join_form\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/600.2.5 (KHTML, like Gecko) Version/8.0.2 Safari/600.2.5\"\n94.242.208.187 - - [27/Oct/2015:18:05:33 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basj.es/join_form HTTP/1.1\" 200 11713 \"http://niels.basj.es/join_form\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/600.2.5 (KHTML, like Gecko) Version/8.0.2 Safari/600.2.5\"\n94.242.208.187 - - [27/Oct/2015:18:05:33 +0100] \"GET /login_form HTTP/1.1\" 200 11544 \"http://niels.basj.es/\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/600.2.5 (KHTML, like Gecko) Version/8.0.2 Safari/600.2.5\"\n94.242.208.187 - - [27/Oct/2015:18:05:35 +0100] \"POST /login_form HTTP/1.1\" 200 18237 \"http://niels.basj.es/login_form\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/600.2.5 (KHTML, like Gecko) Version/8.0.2 Safari/600.2.5\"\n23.236.210.83 - - [27/Oct/2015:18:26:45 +0100] \"GET /splittable-gzip HTTP/1.0\" 200 27869 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n23.236.210.83 - - [27/Oct/2015:18:26:46 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n23.236.210.83 - - [27/Oct/2015:18:26:47 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n170.130.59.233 - - [27/Oct/2015:18:48:56 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n170.130.59.233 - - [27/Oct/2015:18:48:58 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n170.130.59.233 - - [27/Oct/2015:18:49:01 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n170.130.59.233 - - [27/Oct/2015:18:49:03 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n170.130.59.233 - - [27/Oct/2015:18:49:07 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n218.200.66.196 - - [27/Oct/2015:18:51:53 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.0\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n218.200.66.196 - - [27/Oct/2015:18:51:56 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n218.200.66.196 - - [27/Oct/2015:18:51:58 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n218.200.66.196 - - [27/Oct/2015:18:52:00 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n121.204.74.138 - - [27/Oct/2015:18:57:10 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36440 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0 AlexaToolbar/alxf-2.21\"\n121.204.74.138 - - [27/Oct/2015:18:57:15 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0 AlexaToolbar/alxf-2.21\"\n121.204.74.138 - - [27/Oct/2015:18:57:31 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0 AlexaToolbar/alxf-2.21\"\n107.172.0.248 - - [27/Oct/2015:18:59:50 +0100] \"GET /places HTTP/1.0\" 200 19355 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n107.172.0.248 - - [27/Oct/2015:18:59:52 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n107.172.0.248 - - [27/Oct/2015:18:59:53 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n107.172.0.248 - - [27/Oct/2015:18:59:54 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n107.172.0.248 - - [27/Oct/2015:18:59:55 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n107.172.0.248 - - [27/Oct/2015:18:59:57 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n31.220.113.224 - - [27/Oct/2015:19:03:35 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.0\" 200 24319 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n31.220.113.224 - - [27/Oct/2015:19:03:37 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n31.220.113.224 - - [27/Oct/2015:19:03:38 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n31.220.113.224 - - [27/Oct/2015:19:03:38 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n31.220.113.224 - - [27/Oct/2015:19:03:39 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n31.220.113.224 - - [27/Oct/2015:19:03:40 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n89.33.71.136 - - [27/Oct/2015:19:04:30 +0100] \"GET /join_form HTTP/1.0\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n89.33.71.136 - - [27/Oct/2015:19:04:31 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n89.33.71.136 - - [27/Oct/2015:19:04:31 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n89.33.71.136 - - [27/Oct/2015:19:04:32 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n89.33.71.136 - - [27/Oct/2015:19:04:33 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n23.94.236.195 - - [27/Oct/2015:19:16:58 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n23.94.236.195 - - [27/Oct/2015:19:16:59 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n23.94.236.195 - - [27/Oct/2015:19:17:00 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n23.94.236.195 - - [27/Oct/2015:19:17:02 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n23.94.236.195 - - [27/Oct/2015:19:17:03 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n198.52.158.252 - - [27/Oct/2015:19:23:18 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n198.52.158.252 - - [27/Oct/2015:19:23:19 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n198.52.158.252 - - [27/Oct/2015:19:23:20 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n198.52.158.252 - - [27/Oct/2015:19:23:22 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n198.52.158.252 - - [27/Oct/2015:19:23:24 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n89.32.251.246 - - [27/Oct/2015:19:30:28 +0100] \"GET /join_form HTTP/1.0\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n89.32.251.246 - - [27/Oct/2015:19:30:28 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n89.32.251.246 - - [27/Oct/2015:19:30:29 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n89.32.251.246 - - [27/Oct/2015:19:30:30 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n89.32.251.246 - - [27/Oct/2015:19:30:31 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n123.88.10.57 - - [27/Oct/2015:19:40:41 +0100] \"GET /places HTTP/1.1\" 200 19355 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n123.88.10.57 - - [27/Oct/2015:19:41:17 +0100] \"GET /places HTTP/1.1\" 200 19355 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n123.88.10.57 - - [27/Oct/2015:19:41:20 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n123.88.10.57 - - [27/Oct/2015:19:41:25 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n123.88.10.57 - - [27/Oct/2015:19:41:26 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n123.88.10.57 - - [27/Oct/2015:19:41:28 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n123.88.10.57 - - [27/Oct/2015:19:42:03 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n41.109.52.32 - - [27/Oct/2015:20:02:35 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.0\" 200 36440 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n41.109.52.32 - - [27/Oct/2015:20:02:54 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n41.109.52.32 - - [27/Oct/2015:20:03:01 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n188.72.96.104 - - [27/Oct/2015:20:06:51 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.0\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n188.72.96.104 - - [27/Oct/2015:20:06:52 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n66.150.34.163 - - [27/Oct/2015:20:10:06 +0100] \"GET / HTTP/1.1\" 200 20474 \"http://niels.basjes.nl\" \"Mozilla/5.0 (X11; CrOS x86_64 6310.68.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.96 Safari/537.36\"\n66.150.34.163 - - [27/Oct/2015:20:10:08 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl\" \"Mozilla/5.0 (X11; CrOS x86_64 6310.68.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.96 Safari/537.36\"\n66.150.34.163 - - [27/Oct/2015:20:10:09 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (X11; CrOS x86_64 6310.68.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.96 Safari/537.36\"\n66.150.34.163 - - [27/Oct/2015:20:10:09 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (X11; CrOS x86_64 6310.68.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.96 Safari/537.36\"\n66.150.34.163 - - [27/Oct/2015:20:10:10 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (X11; CrOS x86_64 6310.68.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.96 Safari/537.36\"\n66.150.34.163 - - [27/Oct/2015:20:10:11 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (X11; CrOS x86_64 6310.68.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.96 Safari/537.36\"\n187.141.230.35 - - [27/Oct/2015:20:23:24 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.0\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n187.141.230.35 - - [27/Oct/2015:20:23:28 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n187.141.230.35 - - [27/Oct/2015:20:23:33 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n187.141.230.35 - - [27/Oct/2015:20:23:37 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n91.229.229.178 - - [27/Oct/2015:20:24:21 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.0\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n91.229.229.178 - - [27/Oct/2015:20:24:21 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n91.229.229.178 - - [27/Oct/2015:20:24:22 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n91.229.229.178 - - [27/Oct/2015:20:24:23 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n190.82.115.159 - - [27/Oct/2015:20:30:01 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.0\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n190.82.115.159 - - [27/Oct/2015:20:30:25 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n190.82.115.159 - - [27/Oct/2015:20:30:36 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n190.82.115.159 - - [27/Oct/2015:20:30:44 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n104.168.85.254 - - [27/Oct/2015:20:34:44 +0100] \"GET /linux/installing-my-new-server/networking HTTP/1.0\" 200 27448 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n104.168.85.254 - - [27/Oct/2015:20:34:45 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n104.168.85.254 - - [27/Oct/2015:20:34:49 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n104.168.85.254 - - [27/Oct/2015:20:34:50 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n104.168.85.254 - - [27/Oct/2015:20:34:53 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n104.168.85.254 - - [27/Oct/2015:20:34:54 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n69.163.47.119 - - [27/Oct/2015:21:31:41 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n112.14.108.18 - - [27/Oct/2015:21:31:51 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n112.14.108.18 - - [27/Oct/2015:21:32:35 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n183.207.228.40 - - [27/Oct/2015:21:32:39 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n183.207.228.38 - - [27/Oct/2015:21:32:53 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n190.63.175.247 - - [27/Oct/2015:21:32:56 +0100] \"GET /login_form HTTP/1.0\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n117.177.243.42 - - [27/Oct/2015:21:33:11 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n212.83.183.34 - - [27/Oct/2015:21:54:57 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36440 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n212.83.183.34 - - [27/Oct/2015:21:54:58 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n212.83.183.34 - - [27/Oct/2015:21:54:59 +0100] \"POST /join_form HTTP/1.1\" 302 9245 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n212.83.183.34 - - [27/Oct/2015:21:54:59 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n212.83.183.34 - - [27/Oct/2015:21:54:59 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n212.83.183.34 - - [27/Oct/2015:21:55:00 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n23.250.67.105 - - [27/Oct/2015:22:14:16 +0100] \"GET /linux/installing-my-new-server/networking HTTP/1.0\" 200 27448 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n23.250.67.105 - - [27/Oct/2015:22:14:17 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n23.250.67.105 - - [27/Oct/2015:22:14:18 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n23.250.67.105 - - [27/Oct/2015:22:14:20 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n23.250.67.105 - - [27/Oct/2015:22:14:21 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n23.250.67.105 - - [27/Oct/2015:22:14:22 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n91.238.82.193 - - [27/Oct/2015:22:23:38 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.0\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n91.238.82.193 - - [27/Oct/2015:22:23:39 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n91.238.82.193 - - [27/Oct/2015:22:23:40 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n46.161.57.137 - - [27/Oct/2015:22:24:40 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.0\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n46.161.57.137 - - [27/Oct/2015:22:24:41 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n37.230.212.151 - - [27/Oct/2015:22:30:30 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.0\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n37.230.212.151 - - [27/Oct/2015:22:30:31 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n104.223.44.38 - - [27/Oct/2015:22:31:08 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n104.223.44.38 - - [27/Oct/2015:22:31:11 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n104.223.44.38 - - [27/Oct/2015:22:31:13 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n104.223.44.38 - - [27/Oct/2015:22:31:15 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n104.223.44.38 - - [27/Oct/2015:22:31:17 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n104.207.67.234 - - [27/Oct/2015:22:52:09 +0100] \"GET /join_form HTTP/1.0\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n104.207.67.234 - - [27/Oct/2015:22:52:11 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n23.94.10.179 - - [27/Oct/2015:23:05:28 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n23.94.10.179 - - [27/Oct/2015:23:05:29 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n23.94.10.179 - - [27/Oct/2015:23:05:29 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n23.94.10.179 - - [27/Oct/2015:23:05:30 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n23.254.164.173 - - [27/Oct/2015:23:07:16 +0100] \"GET /linux/installing-my-new-server/Various-software/RK=0/RS=C_oF6PJOAKvJ3JsgiwAbhNKI2uE- HTTP/1.1\" 404 12145 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.110 Safari/537.36\"\n23.254.164.173 - - [27/Oct/2015:23:07:17 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/linux/installing-my-new-server/Various-software/RK=0/RS=C_oF6PJOAKvJ3JsgiwAbhNKI2uE-\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.110 Safari/537.36\"\n23.254.164.173 - - [27/Oct/2015:23:07:18 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.110 Safari/537.36\"\n23.254.164.173 - - [27/Oct/2015:23:07:19 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.110 Safari/537.36\"\n23.254.164.173 - - [27/Oct/2015:23:07:20 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.110 Safari/537.36\"\n23.254.164.173 - - [27/Oct/2015:23:07:21 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.110 Safari/537.36\"\n88.150.210.124 - - [27/Oct/2015:23:21:56 +0100] \"GET /join_form HTTP/1.0\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n88.150.210.124 - - [27/Oct/2015:23:21:56 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n88.150.210.124 - - [27/Oct/2015:23:21:56 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n88.150.210.124 - - [27/Oct/2015:23:21:57 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n88.150.210.124 - - [27/Oct/2015:23:21:57 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n162.253.53.198 - - [27/Oct/2015:23:35:27 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36440 \"-\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n162.253.53.198 - - [27/Oct/2015:23:35:29 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"-\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n162.253.53.198 - - [27/Oct/2015:23:35:29 +0100] \"POST /join_form HTTP/1.1\" 302 9245 \"-\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n162.253.53.198 - - [27/Oct/2015:23:35:30 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"-\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n162.253.53.198 - - [27/Oct/2015:23:35:31 +0100] \"GET /login_form HTTP/1.1\" 200 10505 \"-\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n162.253.53.198 - - [27/Oct/2015:23:35:31 +0100] \"POST /login_form HTTP/1.1\" 200 16768 \"-\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n192.3.106.205 - - [27/Oct/2015:23:47:23 +0100] \"GET / HTTP/1.1\" 200 20474 \"http://niels.basjes.nl\" \"Mozilla/5.0 (X11; CrOS x86_64 6310.68.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.96 Safari/537.36\"\n192.3.106.205 - - [27/Oct/2015:23:47:25 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl\" \"Mozilla/5.0 (X11; CrOS x86_64 6310.68.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.96 Safari/537.36\"\n192.3.106.205 - - [27/Oct/2015:23:47:26 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (X11; CrOS x86_64 6310.68.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.96 Safari/537.36\"\n192.3.106.205 - - [27/Oct/2015:23:47:26 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (X11; CrOS x86_64 6310.68.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.96 Safari/537.36\"\n192.3.106.205 - - [27/Oct/2015:23:47:27 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (X11; CrOS x86_64 6310.68.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.96 Safari/537.36\"\n192.3.106.205 - - [27/Oct/2015:23:47:28 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (X11; CrOS x86_64 6310.68.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.96 Safari/537.36\"\n158.222.12.39 - - [27/Oct/2015:23:48:01 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36440 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (X11; CrOS x86_64 6310.68.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.96 Safari/537.36\"\n158.222.12.39 - - [27/Oct/2015:23:48:02 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (X11; CrOS x86_64 6310.68.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.96 Safari/537.36\"\n158.222.12.39 - - [27/Oct/2015:23:48:04 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (X11; CrOS x86_64 6310.68.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.96 Safari/537.36\"\n158.222.12.39 - - [27/Oct/2015:23:48:05 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (X11; CrOS x86_64 6310.68.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.96 Safari/537.36\"\n158.222.12.39 - - [27/Oct/2015:23:48:06 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (X11; CrOS x86_64 6310.68.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.96 Safari/537.36\"\n158.222.12.39 - - [27/Oct/2015:23:48:07 +0100] \"POST /login_form HTTP/1.1\" 200 16806 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (X11; CrOS x86_64 6310.68.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.96 Safari/537.36\"\n31.187.79.201 - - [28/Oct/2015:00:01:53 +0100] \"GET / HTTP/1.0\" 200 20474 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n31.187.79.201 - - [28/Oct/2015:00:01:54 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n31.187.79.201 - - [28/Oct/2015:00:01:54 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n31.187.79.201 - - [28/Oct/2015:00:01:55 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n31.187.79.201 - - [28/Oct/2015:00:01:55 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n31.187.79.201 - - [28/Oct/2015:00:01:56 +0100] \"POST /login_form HTTP/1.1\" 200 18350 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n89.36.65.53 - - [28/Oct/2015:00:10:49 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.0\" 200 24323 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n89.36.65.53 - - [28/Oct/2015:00:10:50 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n89.36.65.53 - - [28/Oct/2015:00:10:52 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n89.36.65.53 - - [28/Oct/2015:00:10:54 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n89.36.65.53 - - [28/Oct/2015:00:10:54 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n89.36.65.53 - - [28/Oct/2015:00:10:55 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n23.94.170.4 - - [28/Oct/2015:00:29:46 +0100] \"GET /places HTTP/1.0\" 200 19359 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n23.94.170.4 - - [28/Oct/2015:00:29:48 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n23.94.170.4 - - [28/Oct/2015:00:29:49 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n23.94.170.4 - - [28/Oct/2015:00:29:50 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n23.94.170.4 - - [28/Oct/2015:00:29:51 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n23.94.170.4 - - [28/Oct/2015:00:29:52 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n31.187.79.201 - - [28/Oct/2015:00:31:27 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.0\" 200 24323 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n31.187.79.201 - - [28/Oct/2015:00:31:29 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n31.187.79.201 - - [28/Oct/2015:00:31:30 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n31.187.79.201 - - [28/Oct/2015:00:31:32 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n31.187.79.201 - - [28/Oct/2015:00:31:33 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n31.187.79.201 - - [28/Oct/2015:00:31:34 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n158.222.12.2 - - [28/Oct/2015:01:00:39 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36444 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (X11; CrOS x86_64 6310.68.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.96 Safari/537.36\"\n158.222.12.2 - - [28/Oct/2015:01:00:41 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (X11; CrOS x86_64 6310.68.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.96 Safari/537.36\"\n158.222.12.2 - - [28/Oct/2015:01:00:43 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (X11; CrOS x86_64 6310.68.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.96 Safari/537.36\"\n158.222.12.2 - - [28/Oct/2015:01:00:44 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (X11; CrOS x86_64 6310.68.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.96 Safari/537.36\"\n158.222.12.2 - - [28/Oct/2015:01:00:45 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (X11; CrOS x86_64 6310.68.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.96 Safari/537.36\"\n158.222.12.2 - - [28/Oct/2015:01:00:46 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (X11; CrOS x86_64 6310.68.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.96 Safari/537.36\"\n155.254.203.202 - - [28/Oct/2015:01:26:21 +0100] \"GET /join_form HTTP/1.0\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n155.254.203.202 - - [28/Oct/2015:01:27:08 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n155.254.203.202 - - [28/Oct/2015:01:27:10 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n155.254.203.202 - - [28/Oct/2015:01:27:45 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n155.254.203.202 - - [28/Oct/2015:01:27:46 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n155.94.140.177 - - [28/Oct/2015:01:39:01 +0100] \"GET /linux/installing-my-new-server/networking HTTP/1.0\" 200 27452 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n155.94.140.177 - - [28/Oct/2015:01:39:03 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n155.94.140.177 - - [28/Oct/2015:01:39:04 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n155.94.140.177 - - [28/Oct/2015:01:39:05 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n155.94.140.177 - - [28/Oct/2015:01:39:06 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n155.94.140.177 - - [28/Oct/2015:01:39:07 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n216.158.201.102 - - [28/Oct/2015:01:40:32 +0100] \"GET /open-source HTTP/1.0\" 200 17529 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n216.158.201.102 - - [28/Oct/2015:01:40:34 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n216.158.201.102 - - [28/Oct/2015:01:40:36 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n216.158.201.102 - - [28/Oct/2015:01:40:37 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n158.222.5.104 - - [28/Oct/2015:01:42:01 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.0\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n158.222.5.104 - - [28/Oct/2015:01:42:02 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n158.222.5.104 - - [28/Oct/2015:01:42:03 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n104.140.71.2 - - [28/Oct/2015:02:46:02 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n104.140.71.2 - - [28/Oct/2015:02:46:03 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n121.204.77.53 - - [28/Oct/2015:02:52:22 +0100] \"GET /linux/installing-my-new-server/networking HTTP/1.1\" 200 27452 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n121.204.77.53 - - [28/Oct/2015:02:52:24 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n121.204.77.53 - - [28/Oct/2015:02:52:25 +0100] \"POST /join_form HTTP/1.1\" 302 9245 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n121.204.77.53 - - [28/Oct/2015:02:52:27 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n121.204.77.53 - - [28/Oct/2015:02:52:28 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n121.204.77.53 - - [28/Oct/2015:02:52:29 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n104.144.19.69 - - [28/Oct/2015:03:00:54 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.0\" 200 24323 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n104.144.19.69 - - [28/Oct/2015:03:00:55 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n104.144.19.69 - - [28/Oct/2015:03:00:57 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n104.144.19.69 - - [28/Oct/2015:03:00:58 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n104.144.19.69 - - [28/Oct/2015:03:01:00 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n104.144.19.69 - - [28/Oct/2015:03:01:01 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n31.220.30.157 - - [28/Oct/2015:03:10:19 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.0\" 200 24323 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n31.220.30.157 - - [28/Oct/2015:03:10:21 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n31.220.30.157 - - [28/Oct/2015:03:10:22 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n31.220.30.157 - - [28/Oct/2015:03:10:23 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n31.220.30.157 - - [28/Oct/2015:03:10:25 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n31.220.30.157 - - [28/Oct/2015:03:10:25 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n31.187.79.201 - - [28/Oct/2015:03:35:03 +0100] \"GET / HTTP/1.0\" 200 20478 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n31.187.79.201 - - [28/Oct/2015:03:35:04 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n31.187.79.201 - - [28/Oct/2015:03:35:05 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n31.187.79.201 - - [28/Oct/2015:03:35:06 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n31.187.79.201 - - [28/Oct/2015:03:35:06 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n31.187.79.201 - - [28/Oct/2015:03:35:07 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n107.150.48.76 - - [28/Oct/2015:03:35:16 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n107.150.48.76 - - [28/Oct/2015:03:35:17 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n94.100.50.54 - - [28/Oct/2015:03:43:12 +0100] \"GET /places HTTP/1.1\" 200 19359 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0\"\n94.100.50.54 - - [28/Oct/2015:03:43:14 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0\"\n5.175.177.178 - - [28/Oct/2015:04:10:28 +0100] \"GET /linux/installing-gitlab-on-centos-6 HTTP/1.0\" 200 19422 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n5.175.177.178 - - [28/Oct/2015:04:10:29 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n5.175.177.178 - - [28/Oct/2015:04:10:31 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n216.158.197.69 - - [28/Oct/2015:04:16:08 +0100] \"GET /open-source HTTP/1.0\" 200 17529 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n216.158.197.69 - - [28/Oct/2015:04:16:10 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n216.158.197.69 - - [28/Oct/2015:04:16:12 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n216.158.197.69 - - [28/Oct/2015:04:16:13 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n88.150.210.124 - - [28/Oct/2015:04:17:05 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n88.150.210.124 - - [28/Oct/2015:04:17:06 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n216.158.211.222 - - [28/Oct/2015:04:19:50 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.0\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n89.33.71.249 - - [28/Oct/2015:04:28:45 +0100] \"GET /join_form HTTP/1.0\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n89.33.71.249 - - [28/Oct/2015:04:28:45 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n89.33.71.249 - - [28/Oct/2015:04:28:47 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n89.33.71.249 - - [28/Oct/2015:04:28:48 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n89.33.71.249 - - [28/Oct/2015:04:28:48 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n31.187.79.201 - - [28/Oct/2015:04:30:12 +0100] \"GET / HTTP/1.0\" 200 20478 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n31.187.79.201 - - [28/Oct/2015:04:30:14 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n31.187.79.201 - - [28/Oct/2015:04:30:14 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n31.187.79.201 - - [28/Oct/2015:04:30:16 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n31.187.79.201 - - [28/Oct/2015:04:30:17 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n31.187.79.201 - - [28/Oct/2015:04:30:18 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n89.42.237.71 - - [28/Oct/2015:04:39:49 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.0\" 200 24323 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n89.42.237.71 - - [28/Oct/2015:04:39:50 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n89.42.237.71 - - [28/Oct/2015:04:39:51 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n89.42.237.71 - - [28/Oct/2015:04:39:52 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n89.42.237.71 - - [28/Oct/2015:04:39:53 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n89.42.237.71 - - [28/Oct/2015:04:39:54 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n180.180.96.208 - - [28/Oct/2015:04:45:56 +0100] \"GET / HTTP/1.1\" 200 20478 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n180.180.96.208 - - [28/Oct/2015:04:45:57 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n180.180.96.208 - - [28/Oct/2015:04:45:59 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n180.180.96.208 - - [28/Oct/2015:04:46:03 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n180.180.96.208 - - [28/Oct/2015:04:46:05 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n180.180.96.208 - - [28/Oct/2015:04:46:07 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n89.42.237.71 - - [28/Oct/2015:04:54:58 +0100] \"GET / HTTP/1.0\" 200 20478 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n89.42.237.71 - - [28/Oct/2015:04:54:59 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n89.42.237.71 - - [28/Oct/2015:04:55:00 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n89.42.237.71 - - [28/Oct/2015:04:55:01 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n89.42.237.71 - - [28/Oct/2015:04:55:02 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n89.42.237.71 - - [28/Oct/2015:04:55:03 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n104.143.28.246 - - [28/Oct/2015:05:08:46 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.0\" 200 36444 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n104.143.28.246 - - [28/Oct/2015:05:08:52 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n104.143.28.246 - - [28/Oct/2015:05:14:19 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n104.143.28.246 - - [28/Oct/2015:05:14:20 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n104.143.28.246 - - [28/Oct/2015:05:14:21 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n104.143.28.246 - - [28/Oct/2015:05:14:23 +0100] \"POST /login_form HTTP/1.1\" 200 16862 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n216.158.211.222 - - [28/Oct/2015:05:36:31 +0100] \"GET /join_form HTTP/1.0\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n216.158.211.222 - - [28/Oct/2015:05:36:34 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n216.158.211.222 - - [28/Oct/2015:05:36:36 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n31.220.30.157 - - [28/Oct/2015:05:36:49 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.0\" 200 24375 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n31.220.30.157 - - [28/Oct/2015:05:36:50 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n31.220.30.157 - - [28/Oct/2015:05:36:51 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n31.220.30.157 - - [28/Oct/2015:05:36:52 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n31.220.30.157 - - [28/Oct/2015:05:36:53 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n31.220.30.157 - - [28/Oct/2015:05:36:53 +0100] \"POST /login_form HTTP/1.1\" 200 16862 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n216.158.197.14 - - [28/Oct/2015:05:39:55 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.0\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n216.158.197.14 - - [28/Oct/2015:05:39:57 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n216.158.197.14 - - [28/Oct/2015:05:39:58 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n14.139.236.210 - - [28/Oct/2015:05:49:30 +0100] \"GET /mail_password_form?userid= HTTP/1.0\" 200 10820 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n14.139.236.210 - - [28/Oct/2015:05:49:34 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n14.139.236.210 - - [28/Oct/2015:05:49:37 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n14.139.236.210 - - [28/Oct/2015:05:49:40 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n192.200.24.243 - - [28/Oct/2015:06:18:25 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.0\" 200 24323 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n192.200.24.243 - - [28/Oct/2015:06:18:28 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n192.200.24.243 - - [28/Oct/2015:06:18:29 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n192.200.24.243 - - [28/Oct/2015:06:18:30 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n192.200.24.243 - - [28/Oct/2015:06:18:32 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n192.200.24.243 - - [28/Oct/2015:06:18:33 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n104.223.44.139 - - [28/Oct/2015:06:19:23 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n104.223.44.139 - - [28/Oct/2015:06:19:24 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n186.27.126.130 - - [28/Oct/2015:06:28:27 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.0\" 200 24323 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n186.27.126.130 - - [28/Oct/2015:06:29:07 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n186.27.126.130 - - [28/Oct/2015:06:29:10 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n186.27.126.130 - - [28/Oct/2015:06:29:12 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n186.27.126.130 - - [28/Oct/2015:06:29:15 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n186.27.126.130 - - [28/Oct/2015:06:29:17 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n107.158.113.117 - - [28/Oct/2015:06:59:04 +0100] \"GET / HTTP/1.1\" 200 20478 \"http://niels.basjes.nl\" \"Mozilla/5.0 (X11; CrOS x86_64 6310.68.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.96 Safari/537.36\"\n107.158.113.117 - - [28/Oct/2015:06:59:05 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl\" \"Mozilla/5.0 (X11; CrOS x86_64 6310.68.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.96 Safari/537.36\"\n107.158.113.117 - - [28/Oct/2015:06:59:07 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (X11; CrOS x86_64 6310.68.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.96 Safari/537.36\"\n107.158.113.117 - - [28/Oct/2015:06:59:08 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (X11; CrOS x86_64 6310.68.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.96 Safari/537.36\"\n107.158.113.117 - - [28/Oct/2015:06:59:10 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (X11; CrOS x86_64 6310.68.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.96 Safari/537.36\"\n107.158.113.117 - - [28/Oct/2015:06:59:12 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (X11; CrOS x86_64 6310.68.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.96 Safari/537.36\"\n79.97.43.126 - - [28/Oct/2015:07:37:02 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n79.97.43.126 - - [28/Oct/2015:07:37:03 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n79.97.43.126 - - [28/Oct/2015:07:37:04 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n104.168.89.17 - - [28/Oct/2015:07:50:27 +0100] \"GET /linux/installing-my-new-server/networking HTTP/1.0\" 200 27452 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n104.168.89.17 - - [28/Oct/2015:07:50:29 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n104.168.89.17 - - [28/Oct/2015:07:50:31 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n216.244.81.34 - - [28/Oct/2015:08:21:47 +0100] \"GET / HTTP/1.1\" 302 281 \"http://basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n216.244.81.34 - - [28/Oct/2015:08:21:48 +0100] \"GET / HTTP/1.1\" 200 20478 \"http://basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n216.244.81.34 - - [28/Oct/2015:08:21:49 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n216.244.81.34 - - [28/Oct/2015:08:21:50 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n216.244.81.34 - - [28/Oct/2015:08:21:51 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n180.180.67.82 - - [28/Oct/2015:08:29:45 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.1\" 200 24323 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n180.180.67.82 - - [28/Oct/2015:08:29:48 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n180.180.67.82 - - [28/Oct/2015:08:29:50 +0100] \"POST /join_form HTTP/1.1\" 302 9245 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n180.180.67.82 - - [28/Oct/2015:08:29:53 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n180.180.67.82 - - [28/Oct/2015:08:29:55 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n180.180.67.82 - - [28/Oct/2015:08:29:57 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n216.244.81.34 - - [28/Oct/2015:08:37:36 +0100] \"GET /linux/installing-my-new-server/vmware-server HTTP/1.1\" 200 25009 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n216.244.81.34 - - [28/Oct/2015:08:37:38 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n216.244.81.34 - - [28/Oct/2015:08:37:39 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n216.244.81.34 - - [28/Oct/2015:08:37:40 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n104.144.250.33 - - [28/Oct/2015:08:43:59 +0100] \"GET /join_form HTTP/1.0\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n104.144.250.33 - - [28/Oct/2015:08:44:01 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n104.144.250.33 - - [28/Oct/2015:08:44:02 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n104.144.250.33 - - [28/Oct/2015:08:44:04 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n104.144.250.33 - - [28/Oct/2015:08:44:05 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n79.97.43.126 - - [28/Oct/2015:08:46:29 +0100] \"GET /places HTTP/1.1\" 200 19359 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n79.97.43.126 - - [28/Oct/2015:08:46:29 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n79.97.43.126 - - [28/Oct/2015:08:46:30 +0100] \"POST /join_form HTTP/1.1\" 302 10322 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n79.97.43.126 - - [28/Oct/2015:08:46:30 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n45.33.159.106 - - [28/Oct/2015:09:10:32 +0100] \"GET /linux/installing-my-new-server/networking HTTP/1.0\" 200 27452 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n45.33.159.106 - - [28/Oct/2015:09:10:34 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n45.33.159.106 - - [28/Oct/2015:09:10:36 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n45.33.159.106 - - [28/Oct/2015:09:10:38 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n45.33.159.106 - - [28/Oct/2015:09:10:39 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n45.33.159.106 - - [28/Oct/2015:09:10:40 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n155.94.141.10 - - [28/Oct/2015:09:38:03 +0100] \"GET /linux/installing-my-new-server/networking HTTP/1.0\" 200 27452 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n155.94.141.10 - - [28/Oct/2015:09:38:13 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n155.94.141.10 - - [28/Oct/2015:09:38:17 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n107.169.2.11 - - [28/Oct/2015:09:48:43 +0100] \"GET / HTTP/1.0\" 200 20478 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n107.169.2.11 - - [28/Oct/2015:09:48:45 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n107.169.2.11 - - [28/Oct/2015:09:48:47 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n216.244.81.34 - - [28/Oct/2015:10:23:09 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n216.244.81.34 - - [28/Oct/2015:10:23:11 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n216.244.81.34 - - [28/Oct/2015:10:23:12 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n198.23.149.114 - - [28/Oct/2015:10:23:35 +0100] \"GET /places HTTP/1.0\" 200 19359 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n198.23.149.114 - - [28/Oct/2015:10:23:39 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n198.23.149.114 - - [28/Oct/2015:10:23:42 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n198.23.149.114 - - [28/Oct/2015:10:23:44 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n198.23.149.114 - - [28/Oct/2015:10:23:46 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n198.23.149.114 - - [28/Oct/2015:10:23:48 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n198.12.82.174 - - [28/Oct/2015:10:40:45 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36444 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n198.12.82.174 - - [28/Oct/2015:10:40:47 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n198.12.82.174 - - [28/Oct/2015:10:40:48 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n198.12.82.174 - - [28/Oct/2015:10:40:49 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n198.12.82.174 - - [28/Oct/2015:10:40:50 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n198.12.82.174 - - [28/Oct/2015:10:40:51 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n185.104.217.140 - - [28/Oct/2015:10:41:08 +0100] \"GET /open-source HTTP/1.1\" 200 17529 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0 AlexaToolbar/alxf-2.21\"\n185.104.217.140 - - [28/Oct/2015:10:41:10 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0 AlexaToolbar/alxf-2.21\"\n185.104.217.140 - - [28/Oct/2015:10:41:11 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0 AlexaToolbar/alxf-2.21\"\n185.104.217.140 - - [28/Oct/2015:10:41:12 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0 AlexaToolbar/alxf-2.21\"\n185.104.217.140 - - [28/Oct/2015:10:41:14 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0 AlexaToolbar/alxf-2.21\"\n185.104.217.140 - - [28/Oct/2015:10:41:15 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0 AlexaToolbar/alxf-2.21\"\n119.73.118.1 - - [28/Oct/2015:10:42:04 +0100] \"GET /places HTTP/1.0\" 200 19359 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n119.73.118.1 - - [28/Oct/2015:10:42:05 +0100] \"GET /join_form HTTP/1.0\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n119.73.118.1 - - [28/Oct/2015:10:42:06 +0100] \"POST /join_form HTTP/1.0\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n119.73.118.1 - - [28/Oct/2015:10:42:07 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.0\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n119.73.118.1 - - [28/Oct/2015:10:42:08 +0100] \"GET /login_form HTTP/1.0\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n119.73.118.1 - - [28/Oct/2015:10:42:09 +0100] \"POST /login_form HTTP/1.0\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n158.222.5.104 - - [28/Oct/2015:10:45:49 +0100] \"GET /open-source HTTP/1.0\" 200 17529 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n158.222.5.104 - - [28/Oct/2015:10:45:51 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n158.222.5.104 - - [28/Oct/2015:10:45:52 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n79.97.43.126 - - [28/Oct/2015:10:46:32 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n79.97.43.126 - - [28/Oct/2015:10:46:32 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n79.97.43.126 - - [28/Oct/2015:10:46:32 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n216.244.81.34 - - [28/Oct/2015:10:59:44 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n216.244.81.34 - - [28/Oct/2015:10:59:44 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n216.244.81.34 - - [28/Oct/2015:10:59:45 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n104.227.92.78 - - [28/Oct/2015:11:00:53 +0100] \"GET /linux/installing-my-new-server/vmware-server HTTP/1.0\" 200 25009 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n104.227.92.78 - - [28/Oct/2015:11:00:54 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n104.227.92.78 - - [28/Oct/2015:11:00:55 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n104.227.92.78 - - [28/Oct/2015:11:00:56 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n104.227.92.78 - - [28/Oct/2015:11:00:58 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n107.158.100.158 - - [28/Oct/2015:11:05:23 +0100] \"GET / HTTP/1.1\" 200 20478 \"http://niels.basjes.nl\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n107.158.100.158 - - [28/Oct/2015:11:05:24 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n107.158.100.158 - - [28/Oct/2015:11:05:25 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n107.158.100.158 - - [28/Oct/2015:11:05:26 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n107.158.100.158 - - [28/Oct/2015:11:05:27 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n107.158.100.158 - - [28/Oct/2015:11:05:28 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n94.23.33.25 - - [28/Oct/2015:11:22:56 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36444 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n158.222.10.221 - - [28/Oct/2015:11:22:57 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n158.222.10.68 - - [28/Oct/2015:11:22:59 +0100] \"POST /join_form HTTP/1.0\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n94.23.33.25 - - [28/Oct/2015:11:25:26 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n204.44.112.144 - - [28/Oct/2015:11:25:27 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.0\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n94.23.33.25 - - [28/Oct/2015:11:29:11 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n204.44.112.144 - - [28/Oct/2015:11:29:12 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n204.44.112.144 - - [28/Oct/2015:11:29:14 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n204.44.112.144 - - [28/Oct/2015:11:29:15 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n204.44.112.144 - - [28/Oct/2015:11:29:16 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n204.44.112.144 - - [28/Oct/2015:11:29:18 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n94.23.33.25 - - [28/Oct/2015:11:32:24 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n204.44.112.144 - - [28/Oct/2015:11:32:25 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n204.44.112.144 - - [28/Oct/2015:11:32:27 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n204.44.112.144 - - [28/Oct/2015:11:32:28 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n204.44.112.144 - - [28/Oct/2015:11:32:29 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n204.44.112.144 - - [28/Oct/2015:11:32:31 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n94.23.33.25 - - [28/Oct/2015:11:38:15 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n158.222.10.131 - - [28/Oct/2015:11:38:16 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.0\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n94.23.33.25 - - [28/Oct/2015:11:44:38 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n204.152.206.187 - - [28/Oct/2015:11:44:39 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n204.152.206.187 - - [28/Oct/2015:11:44:41 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n204.152.206.187 - - [28/Oct/2015:11:44:42 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n204.152.206.187 - - [28/Oct/2015:11:44:44 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n204.152.206.187 - - [28/Oct/2015:11:44:45 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n45.72.9.89 - - [28/Oct/2015:11:48:21 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36444 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n45.72.9.89 - - [28/Oct/2015:11:48:24 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n45.72.9.89 - - [28/Oct/2015:11:48:25 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n45.72.9.89 - - [28/Oct/2015:11:48:26 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n45.72.9.89 - - [28/Oct/2015:11:48:28 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n45.72.9.89 - - [28/Oct/2015:11:48:29 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n94.23.33.25 - - [28/Oct/2015:11:55:19 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n198.52.204.252 - - [28/Oct/2015:11:55:20 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n198.52.204.252 - - [28/Oct/2015:11:55:23 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n198.52.204.252 - - [28/Oct/2015:11:55:30 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n198.52.204.252 - - [28/Oct/2015:11:55:31 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n198.52.204.252 - - [28/Oct/2015:11:55:32 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n94.23.33.25 - - [28/Oct/2015:11:59:23 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n198.52.204.252 - - [28/Oct/2015:11:59:24 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.0\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n91.99.99.59 - - [28/Oct/2015:12:01:20 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n91.99.99.59 - - [28/Oct/2015:12:01:25 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n91.99.99.59 - - [28/Oct/2015:12:01:26 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n91.99.99.59 - - [28/Oct/2015:12:01:28 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n94.23.33.25 - - [28/Oct/2015:12:05:03 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n158.222.10.68 - - [28/Oct/2015:12:05:04 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.0\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n23.231.24.101 - - [28/Oct/2015:12:06:02 +0100] \"GET / HTTP/1.1\" 200 20478 \"http://niels.basjes.nl\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n23.231.24.101 - - [28/Oct/2015:12:06:03 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n23.231.24.101 - - [28/Oct/2015:12:06:04 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n23.231.24.101 - - [28/Oct/2015:12:06:05 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n23.231.24.101 - - [28/Oct/2015:12:06:06 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n23.231.24.101 - - [28/Oct/2015:12:06:07 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n154.16.72.210 - - [28/Oct/2015:12:08:59 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.0\" 200 24323 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n154.16.72.210 - - [28/Oct/2015:12:08:59 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n154.16.72.210 - - [28/Oct/2015:12:09:00 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n154.16.72.210 - - [28/Oct/2015:12:09:01 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n154.16.72.210 - - [28/Oct/2015:12:09:01 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n154.16.72.210 - - [28/Oct/2015:12:09:02 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n94.23.33.25 - - [28/Oct/2015:12:09:24 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n158.222.10.68 - - [28/Oct/2015:12:09:25 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.0\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n94.23.33.25 - - [28/Oct/2015:12:15:07 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n69.12.79.105 - - [28/Oct/2015:12:15:08 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.0\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n94.23.33.25 - - [28/Oct/2015:12:23:11 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n107.158.89.184 - - [28/Oct/2015:12:23:12 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.0\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n107.161.84.31 - - [28/Oct/2015:12:26:20 +0100] \"GET /join_form HTTP/1.0\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n107.161.84.31 - - [28/Oct/2015:12:26:21 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n107.161.84.31 - - [28/Oct/2015:12:26:23 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n107.161.84.31 - - [28/Oct/2015:12:26:24 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n107.161.84.31 - - [28/Oct/2015:12:26:26 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n94.23.33.25 - - [28/Oct/2015:12:28:39 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n198.12.117.57 - - [28/Oct/2015:12:28:40 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n198.12.117.57 - - [28/Oct/2015:12:28:42 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n198.12.117.57 - - [28/Oct/2015:12:28:43 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n198.12.117.57 - - [28/Oct/2015:12:28:45 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n198.12.117.57 - - [28/Oct/2015:12:28:47 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n94.23.33.25 - - [28/Oct/2015:12:31:16 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n198.12.117.57 - - [28/Oct/2015:12:31:18 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.0\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n173.44.194.237 - - [28/Oct/2015:13:09:06 +0100] \"GET /linux/installing-my-new-server/networking HTTP/1.1\" 200 27452 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n173.44.194.237 - - [28/Oct/2015:13:09:10 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n173.44.194.237 - - [28/Oct/2015:13:09:12 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n173.44.194.237 - - [28/Oct/2015:13:09:14 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n192.200.24.243 - - [28/Oct/2015:13:09:58 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.0\" 200 24323 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n192.200.24.243 - - [28/Oct/2015:13:09:59 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n192.200.24.243 - - [28/Oct/2015:13:10:01 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n192.200.24.243 - - [28/Oct/2015:13:10:03 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n192.200.24.243 - - [28/Oct/2015:13:10:04 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n192.200.24.243 - - [28/Oct/2015:13:10:05 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n188.211.162.129 - - [28/Oct/2015:13:11:27 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.0\" 200 36444 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n188.211.162.129 - - [28/Oct/2015:13:11:28 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n188.211.162.129 - - [28/Oct/2015:13:11:28 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n188.211.162.129 - - [28/Oct/2015:13:11:29 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n188.211.162.129 - - [28/Oct/2015:13:11:30 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n188.211.162.129 - - [28/Oct/2015:13:11:31 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n104.218.192.205 - - [28/Oct/2015:13:12:27 +0100] \"GET /linux/installing-gitlab-on-centos-6 HTTP/1.0\" 200 19422 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n104.218.192.205 - - [28/Oct/2015:13:12:28 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n104.218.192.205 - - [28/Oct/2015:13:12:29 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n104.218.192.205 - - [28/Oct/2015:13:12:30 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n104.218.192.205 - - [28/Oct/2015:13:12:31 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n104.218.192.205 - - [28/Oct/2015:13:12:32 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n198.23.234.188 - - [28/Oct/2015:13:31:14 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.0\" 200 36444 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n198.23.234.188 - - [28/Oct/2015:13:31:16 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n198.23.234.188 - - [28/Oct/2015:13:31:17 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n198.23.234.188 - - [28/Oct/2015:13:31:18 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n198.23.234.188 - - [28/Oct/2015:13:31:20 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n198.23.234.188 - - [28/Oct/2015:13:31:21 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n46.102.99.141 - - [28/Oct/2015:13:39:56 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basj.es/join_form HTTP/1.0\" 200 11713 \"http://niels.basj.es/\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n46.102.99.141 - - [28/Oct/2015:13:39:57 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basj.es/join_form HTTP/1.1\" 200 11713 \"http://niels.basj.es/\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n195.154.240.173 - - [28/Oct/2015:13:44:29 +0100] \"GET /places HTTP/1.1\" 200 19359 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n195.154.240.173 - - [28/Oct/2015:13:44:30 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n195.154.240.173 - - [28/Oct/2015:13:44:30 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n195.154.240.173 - - [28/Oct/2015:13:44:31 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n195.154.240.173 - - [28/Oct/2015:13:44:31 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n195.154.240.173 - - [28/Oct/2015:13:44:31 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n180.180.108.65 - - [28/Oct/2015:13:49:25 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.1\" 200 24323 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n180.180.108.65 - - [28/Oct/2015:13:49:27 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n180.180.108.65 - - [28/Oct/2015:13:49:31 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n180.180.108.65 - - [28/Oct/2015:13:49:33 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n180.180.108.65 - - [28/Oct/2015:13:49:34 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n180.180.108.65 - - [28/Oct/2015:13:49:36 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n23.95.42.226 - - [28/Oct/2015:14:03:36 +0100] \"GET / HTTP/1.0\" 200 20478 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n23.95.42.226 - - [28/Oct/2015:14:03:37 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n23.95.42.226 - - [28/Oct/2015:14:03:39 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n23.95.42.226 - - [28/Oct/2015:14:03:40 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n23.95.42.226 - - [28/Oct/2015:14:03:41 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n23.95.42.226 - - [28/Oct/2015:14:03:42 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n66.150.34.163 - - [28/Oct/2015:14:04:59 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36444 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n66.150.34.163 - - [28/Oct/2015:14:05:01 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n66.150.34.163 - - [28/Oct/2015:14:05:02 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n66.150.34.163 - - [28/Oct/2015:14:05:03 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n66.150.34.163 - - [28/Oct/2015:14:05:04 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n66.150.34.163 - - [28/Oct/2015:14:05:05 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n88.150.210.88 - - [28/Oct/2015:14:15:08 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n88.150.210.88 - - [28/Oct/2015:14:15:09 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n107.161.84.59 - - [28/Oct/2015:14:19:51 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n107.161.84.59 - - [28/Oct/2015:14:19:53 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n107.161.84.59 - - [28/Oct/2015:14:19:54 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n107.161.84.59 - - [28/Oct/2015:14:19:56 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n107.161.84.59 - - [28/Oct/2015:14:19:57 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n173.44.194.237 - - [28/Oct/2015:14:33:40 +0100] \"GET /linux/installing-my-new-server/vmware-server HTTP/1.1\" 200 25009 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n173.44.194.237 - - [28/Oct/2015:14:33:45 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n173.44.194.237 - - [28/Oct/2015:14:33:49 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n173.44.194.237 - - [28/Oct/2015:14:33:54 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n173.44.194.237 - - [28/Oct/2015:15:14:24 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n173.44.194.237 - - [28/Oct/2015:15:14:26 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n173.44.194.237 - - [28/Oct/2015:15:14:29 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n154.16.72.210 - - [28/Oct/2015:15:31:26 +0100] \"GET / HTTP/1.0\" 200 20478 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n154.16.72.210 - - [28/Oct/2015:15:31:28 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n154.16.72.210 - - [28/Oct/2015:15:31:28 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n154.16.72.210 - - [28/Oct/2015:15:31:29 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n154.16.72.210 - - [28/Oct/2015:15:31:30 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n154.16.72.210 - - [28/Oct/2015:15:31:31 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n155.94.194.105 - - [28/Oct/2015:15:43:50 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.0\" 200 36444 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n155.94.194.105 - - [28/Oct/2015:15:43:52 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n155.94.194.105 - - [28/Oct/2015:15:43:53 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n155.94.194.105 - - [28/Oct/2015:15:43:54 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n155.94.194.105 - - [28/Oct/2015:15:43:56 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n155.94.194.105 - - [28/Oct/2015:15:43:57 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n173.44.194.237 - - [28/Oct/2015:15:48:59 +0100] \"GET /linux/installing-gitlab-on-centos-6 HTTP/1.1\" 200 19422 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n173.44.194.237 - - [28/Oct/2015:15:49:02 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n173.44.194.237 - - [28/Oct/2015:15:49:04 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n173.44.194.237 - - [28/Oct/2015:15:49:07 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n31.220.113.224 - - [28/Oct/2015:15:52:12 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.0\" 200 24323 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n31.220.113.224 - - [28/Oct/2015:15:52:13 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n31.220.113.224 - - [28/Oct/2015:15:52:14 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n31.220.113.224 - - [28/Oct/2015:15:52:15 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n31.220.113.224 - - [28/Oct/2015:15:52:16 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n31.220.113.224 - - [28/Oct/2015:15:52:17 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n173.44.194.237 - - [28/Oct/2015:16:00:08 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64/My-first-howto HTTP/1.1\" 200 20024 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n173.44.194.237 - - [28/Oct/2015:16:00:11 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n173.44.194.237 - - [28/Oct/2015:16:00:13 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n173.44.194.237 - - [28/Oct/2015:16:00:14 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n31.220.113.224 - - [28/Oct/2015:16:02:38 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.0\" 200 24323 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n31.220.113.224 - - [28/Oct/2015:16:02:39 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n31.220.113.224 - - [28/Oct/2015:16:02:40 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n31.220.113.224 - - [28/Oct/2015:16:02:41 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n31.220.113.224 - - [28/Oct/2015:16:02:42 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n31.220.113.224 - - [28/Oct/2015:16:02:43 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n173.44.194.237 - - [28/Oct/2015:16:37:38 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n173.44.194.237 - - [28/Oct/2015:16:37:40 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n173.44.194.237 - - [28/Oct/2015:16:37:42 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n212.83.141.47 - - [28/Oct/2015:16:40:42 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n198.144.182.150 - - [28/Oct/2015:16:40:43 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.0\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n31.220.30.157 - - [28/Oct/2015:16:41:00 +0100] \"GET / HTTP/1.0\" 200 20478 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n31.220.30.157 - - [28/Oct/2015:16:41:01 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n31.220.30.157 - - [28/Oct/2015:16:41:02 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n31.220.30.157 - - [28/Oct/2015:16:41:04 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n31.220.30.157 - - [28/Oct/2015:16:41:05 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n31.220.30.157 - - [28/Oct/2015:16:41:06 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n113.140.43.51 - - [28/Oct/2015:16:47:13 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.0\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n113.140.43.51 - - [28/Oct/2015:16:47:25 +0100] \"GET /login_form HTTP/1.0\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n104.254.212.109 - - [28/Oct/2015:16:54:09 +0100] \"GET / HTTP/1.1\" 200 20478 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n104.254.212.109 - - [28/Oct/2015:16:54:11 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n104.254.212.109 - - [28/Oct/2015:16:54:12 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n104.254.212.109 - - [28/Oct/2015:16:54:13 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n104.254.212.109 - - [28/Oct/2015:16:54:14 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n104.254.212.109 - - [28/Oct/2015:16:54:16 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n46.17.57.54 - - [28/Oct/2015:16:55:30 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36444 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n46.17.57.54 - - [28/Oct/2015:16:55:31 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n46.17.57.54 - - [28/Oct/2015:16:55:32 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n46.17.57.54 - - [28/Oct/2015:16:55:32 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n46.17.57.54 - - [28/Oct/2015:16:55:33 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n46.17.57.54 - - [28/Oct/2015:16:55:35 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n173.44.194.237 - - [28/Oct/2015:17:09:14 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36444 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n173.44.194.237 - - [28/Oct/2015:17:09:17 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n173.44.194.237 - - [28/Oct/2015:17:09:19 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n173.44.194.237 - - [28/Oct/2015:17:09:21 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n158.222.5.244 - - [28/Oct/2015:17:09:56 +0100] \"GET /open-source HTTP/1.0\" 200 17529 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n158.222.5.244 - - [28/Oct/2015:17:09:58 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n158.222.5.244 - - [28/Oct/2015:17:09:59 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n158.222.5.244 - - [28/Oct/2015:17:10:00 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n173.208.36.85 - - [28/Oct/2015:17:15:23 +0100] \"GET /linux/installing-my-new-server/networking HTTP/1.0\" 200 27452 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n173.208.36.85 - - [28/Oct/2015:17:15:29 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n173.208.36.85 - - [28/Oct/2015:17:15:33 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n173.208.36.85 - - [28/Oct/2015:17:15:35 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n173.208.36.85 - - [28/Oct/2015:17:15:37 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n173.208.36.85 - - [28/Oct/2015:17:15:39 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n216.158.200.122 - - [28/Oct/2015:17:18:09 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.0\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n216.158.200.122 - - [28/Oct/2015:17:18:11 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n216.158.200.122 - - [28/Oct/2015:17:18:12 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n31.187.79.201 - - [28/Oct/2015:17:31:28 +0100] \"GET / HTTP/1.0\" 200 20478 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n31.187.79.201 - - [28/Oct/2015:17:31:31 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n31.187.79.201 - - [28/Oct/2015:17:31:32 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n31.187.79.201 - - [28/Oct/2015:17:31:32 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n31.187.79.201 - - [28/Oct/2015:17:31:33 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n31.187.79.201 - - [28/Oct/2015:17:31:34 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n190.235.197.227 - - [28/Oct/2015:17:43:43 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basj.es/join_form HTTP/1.1\" 200 11713 \"http://niels.basj.es/\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n190.235.197.227 - - [28/Oct/2015:17:43:58 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basj.es/join_form HTTP/1.1\" 200 11713 \"http://niels.basj.es/\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n173.44.194.237 - - [28/Oct/2015:17:50:07 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n173.44.194.237 - - [28/Oct/2015:17:50:09 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n173.44.194.237 - - [28/Oct/2015:17:50:10 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n173.44.194.237 - - [28/Oct/2015:17:59:51 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n173.44.194.237 - - [28/Oct/2015:17:59:53 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n173.44.194.237 - - [28/Oct/2015:17:59:55 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n167.160.127.116 - - [28/Oct/2015:18:03:22 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n167.160.127.116 - - [28/Oct/2015:18:03:23 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n104.144.132.58 - - [28/Oct/2015:18:52:45 +0100] \"GET / HTTP/1.1\" 200 20478 \"http://niels.basjes.nl\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n104.144.132.58 - - [28/Oct/2015:18:52:46 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n104.144.132.58 - - [28/Oct/2015:18:52:47 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n104.144.132.58 - - [28/Oct/2015:18:52:48 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n104.144.132.58 - - [28/Oct/2015:18:52:49 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n104.144.132.58 - - [28/Oct/2015:18:52:49 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n66.248.205.240 - - [28/Oct/2015:18:56:17 +0100] \"GET /places HTTP/1.0\" 200 19359 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n66.248.205.240 - - [28/Oct/2015:18:56:18 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n66.248.205.240 - - [28/Oct/2015:18:56:19 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n66.248.205.240 - - [28/Oct/2015:18:56:23 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n216.158.199.158 - - [28/Oct/2015:19:06:15 +0100] \"GET /open-source HTTP/1.0\" 200 17529 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n216.158.199.158 - - [28/Oct/2015:19:06:17 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n216.158.199.158 - - [28/Oct/2015:19:06:19 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n216.158.199.158 - - [28/Oct/2015:19:06:21 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n173.44.194.237 - - [28/Oct/2015:19:10:00 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n173.44.194.237 - - [28/Oct/2015:19:10:02 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n173.44.194.237 - - [28/Oct/2015:19:10:06 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n216.158.201.102 - - [28/Oct/2015:19:16:23 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.0\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n216.158.201.102 - - [28/Oct/2015:19:16:25 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n216.158.201.102 - - [28/Oct/2015:19:16:27 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n104.247.99.226 - - [28/Oct/2015:19:21:53 +0100] \"GET / HTTP/1.0\" 200 20478 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n104.247.99.226 - - [28/Oct/2015:19:21:56 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n104.247.99.226 - - [28/Oct/2015:19:21:58 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n104.247.99.226 - - [28/Oct/2015:19:22:00 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n104.247.99.226 - - [28/Oct/2015:19:22:01 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n104.247.99.226 - - [28/Oct/2015:19:22:03 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n148.251.65.34 - - [28/Oct/2015:19:23:49 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36444 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n45.57.165.41 - - [28/Oct/2015:19:23:50 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n45.57.165.41 - - [28/Oct/2015:19:23:51 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n45.57.165.41 - - [28/Oct/2015:19:23:53 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n45.57.165.41 - - [28/Oct/2015:19:23:54 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n45.57.165.41 - - [28/Oct/2015:19:23:55 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n107.169.2.207 - - [28/Oct/2015:19:46:15 +0100] \"GET / HTTP/1.0\" 200 20478 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n107.169.2.207 - - [28/Oct/2015:19:46:22 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n107.169.2.207 - - [28/Oct/2015:19:46:24 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n192.227.173.86 - - [28/Oct/2015:19:48:02 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.0\" 200 36444 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n192.227.173.86 - - [28/Oct/2015:19:48:03 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n192.227.173.86 - - [28/Oct/2015:19:48:05 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n192.227.173.86 - - [28/Oct/2015:19:48:06 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n192.227.173.86 - - [28/Oct/2015:19:48:07 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n192.227.173.86 - - [28/Oct/2015:19:48:08 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n104.144.19.69 - - [28/Oct/2015:19:49:48 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.0\" 200 24323 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n104.144.19.69 - - [28/Oct/2015:19:49:51 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n104.144.19.69 - - [28/Oct/2015:19:49:52 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n104.144.19.69 - - [28/Oct/2015:19:49:54 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n104.144.19.69 - - [28/Oct/2015:19:49:55 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n104.144.19.69 - - [28/Oct/2015:19:49:57 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n198.46.229.187 - - [28/Oct/2015:19:50:14 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.1\" 200 24323 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0 AlexaToolbar/alxf-2.21\"\n198.46.229.187 - - [28/Oct/2015:19:50:15 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0 AlexaToolbar/alxf-2.21\"\n198.46.229.187 - - [28/Oct/2015:19:50:16 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0 AlexaToolbar/alxf-2.21\"\n198.46.229.187 - - [28/Oct/2015:19:50:17 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0 AlexaToolbar/alxf-2.21\"\n198.46.229.187 - - [28/Oct/2015:19:50:18 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0 AlexaToolbar/alxf-2.21\"\n198.46.229.187 - - [28/Oct/2015:19:50:19 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0 AlexaToolbar/alxf-2.21\"\n23.250.67.33 - - [28/Oct/2015:20:03:26 +0100] \"GET /linux/installing-my-new-server/networking HTTP/1.0\" 200 27452 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n23.250.67.33 - - [28/Oct/2015:20:03:28 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n23.250.67.33 - - [28/Oct/2015:20:03:29 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n23.250.67.33 - - [28/Oct/2015:20:03:30 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n23.250.67.33 - - [28/Oct/2015:20:03:32 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n23.250.67.33 - - [28/Oct/2015:20:03:33 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n172.245.229.166 - - [28/Oct/2015:20:07:50 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n172.245.229.166 - - [28/Oct/2015:20:07:51 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n172.245.229.166 - - [28/Oct/2015:20:07:52 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n172.245.229.166 - - [28/Oct/2015:20:07:53 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n23.95.195.114 - - [28/Oct/2015:20:08:03 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n23.95.195.114 - - [28/Oct/2015:20:08:04 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n23.95.195.114 - - [28/Oct/2015:20:08:06 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n23.95.195.114 - - [28/Oct/2015:20:08:07 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n23.95.195.114 - - [28/Oct/2015:20:08:08 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n195.154.46.135 - - [28/Oct/2015:20:19:59 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.1\" 200 24323 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n192.3.251.47 - - [28/Oct/2015:20:20:01 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n192.3.251.47 - - [28/Oct/2015:20:20:02 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n192.3.251.47 - - [28/Oct/2015:20:20:04 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n195.154.46.135 - - [28/Oct/2015:20:52:00 +0100] \"GET /open-source HTTP/1.1\" 200 17529 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n23.236.191.213 - - [28/Oct/2015:20:52:01 +0100] \"GET /join_form HTTP/1.0\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n23.236.191.213 - - [28/Oct/2015:20:52:02 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n23.236.191.213 - - [28/Oct/2015:20:52:04 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n192.99.244.139 - - [28/Oct/2015:20:54:03 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36444 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n185.68.111.223 - - [28/Oct/2015:21:08:29 +0100] \"GET /work HTTP/1.1\" 200 17885 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n45.72.7.195 - - [28/Oct/2015:21:08:31 +0100] \"GET /join_form HTTP/1.0\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n45.72.7.195 - - [28/Oct/2015:21:08:33 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n45.72.7.195 - - [28/Oct/2015:21:08:34 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n192.161.195.201 - - [28/Oct/2015:21:10:52 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n192.161.195.201 - - [28/Oct/2015:21:10:53 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n148.251.65.34 - - [28/Oct/2015:21:20:18 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36444 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n158.222.10.84 - - [28/Oct/2015:21:20:30 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n50.2.28.99 - - [28/Oct/2015:21:20:32 +0100] \"POST /join_form HTTP/1.0\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n50.2.28.99 - - [28/Oct/2015:21:20:33 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.0\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n50.2.28.99 - - [28/Oct/2015:21:20:34 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n50.2.28.99 - - [28/Oct/2015:21:20:36 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n195.154.46.135 - - [28/Oct/2015:21:26:29 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.1\" 200 24323 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n23.236.191.213 - - [28/Oct/2015:21:26:30 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n23.236.191.213 - - [28/Oct/2015:21:26:31 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n23.236.191.213 - - [28/Oct/2015:21:26:32 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n158.222.12.116 - - [28/Oct/2015:21:31:16 +0100] \"GET / HTTP/1.1\" 200 20478 \"http://niels.basjes.nl\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n158.222.12.116 - - [28/Oct/2015:21:31:21 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n158.222.12.116 - - [28/Oct/2015:21:31:24 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n158.222.12.116 - - [28/Oct/2015:21:31:28 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n158.222.12.116 - - [28/Oct/2015:21:31:31 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n158.222.12.116 - - [28/Oct/2015:21:31:33 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n190.75.196.158 - - [28/Oct/2015:21:44:32 +0100] \"GET /places HTTP/1.0\" 200 19359 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n190.75.196.158 - - [28/Oct/2015:21:44:42 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n190.75.196.158 - - [28/Oct/2015:21:44:57 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n107.173.192.152 - - [28/Oct/2015:21:59:09 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36444 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n107.173.192.152 - - [28/Oct/2015:21:59:11 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n107.173.192.152 - - [28/Oct/2015:21:59:13 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n107.173.192.152 - - [28/Oct/2015:21:59:14 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n107.173.192.152 - - [28/Oct/2015:21:59:16 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n107.173.192.152 - - [28/Oct/2015:21:59:17 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n185.68.111.223 - - [28/Oct/2015:22:08:43 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n155.94.68.30 - - [28/Oct/2015:22:31:47 +0100] \"GET /open-source HTTP/1.0\" 200 17529 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n155.94.68.30 - - [28/Oct/2015:22:31:49 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n155.94.68.30 - - [28/Oct/2015:22:31:50 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n155.94.68.30 - - [28/Oct/2015:22:31:51 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n155.94.68.30 - - [28/Oct/2015:22:31:53 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n155.94.68.30 - - [28/Oct/2015:22:31:54 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n195.154.46.135 - - [28/Oct/2015:22:33:51 +0100] \"GET /open-source HTTP/1.1\" 200 17529 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n23.95.100.64 - - [28/Oct/2015:22:33:52 +0100] \"GET /join_form HTTP/1.0\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n23.95.100.64 - - [28/Oct/2015:22:33:54 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n195.154.46.135 - - [28/Oct/2015:22:38:59 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n104.144.28.48 - - [28/Oct/2015:22:39:44 +0100] \"GET / HTTP/1.0\" 200 17057 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n104.144.28.48 - - [28/Oct/2015:22:39:45 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n104.144.28.48 - - [28/Oct/2015:22:39:47 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n104.144.28.48 - - [28/Oct/2015:22:39:48 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n104.144.28.48 - - [28/Oct/2015:22:39:49 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n104.144.28.48 - - [28/Oct/2015:22:39:51 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n192.161.195.211 - - [28/Oct/2015:22:55:55 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n192.161.195.211 - - [28/Oct/2015:22:55:56 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n192.161.195.211 - - [28/Oct/2015:22:55:57 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n88.150.236.121 - - [28/Oct/2015:23:00:56 +0100] \"GET /accessibility-info HTTP/1.1\" 200 20626 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n88.150.236.121 - - [28/Oct/2015:23:00:57 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n88.150.236.121 - - [28/Oct/2015:23:00:57 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n88.150.236.121 - - [28/Oct/2015:23:00:58 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n88.150.236.121 - - [28/Oct/2015:23:00:58 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n88.150.236.121 - - [28/Oct/2015:23:00:59 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n195.154.46.135 - - [28/Oct/2015:23:29:06 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n23.247.146.31 - - [28/Oct/2015:23:29:07 +0100] \"GET /login_form HTTP/1.0\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n23.247.146.31 - - [28/Oct/2015:23:29:08 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n195.154.46.135 - - [28/Oct/2015:23:32:25 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.1\" 200 24323 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n104.223.29.240 - - [28/Oct/2015:23:32:27 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n104.223.29.240 - - [28/Oct/2015:23:32:28 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n195.154.46.135 - - [28/Oct/2015:23:40:53 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n195.154.46.135 - - [28/Oct/2015:23:47:13 +0100] \"GET /open-source HTTP/1.1\" 200 17529 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n23.247.146.31 - - [28/Oct/2015:23:47:14 +0100] \"GET /join_form HTTP/1.0\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n66.248.206.196 - - [28/Oct/2015:23:47:15 +0100] \"POST /join_form HTTP/1.0\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n66.248.206.196 - - [28/Oct/2015:23:47:16 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.0\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n104.140.71.44 - - [28/Oct/2015:23:51:07 +0100] \"GET / HTTP/1.1\" 200 20478 \"http://niels.basjes.nl\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n104.140.71.44 - - [28/Oct/2015:23:51:09 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n104.140.71.44 - - [28/Oct/2015:23:51:10 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n104.140.71.44 - - [28/Oct/2015:23:51:11 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n104.140.71.44 - - [28/Oct/2015:23:51:12 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n104.140.71.44 - - [28/Oct/2015:23:51:13 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n104.168.75.193 - - [29/Oct/2015:00:03:54 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n104.168.75.193 - - [29/Oct/2015:00:03:55 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n104.168.75.193 - - [29/Oct/2015:00:03:57 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n104.168.75.193 - - [29/Oct/2015:00:03:58 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n104.168.75.193 - - [29/Oct/2015:00:03:59 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n167.160.127.116 - - [29/Oct/2015:00:21:33 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n167.160.127.116 - - [29/Oct/2015:00:21:34 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n148.251.65.34 - - [29/Oct/2015:00:28:37 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36444 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n107.158.89.233 - - [29/Oct/2015:00:28:38 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n107.158.89.247 - - [29/Oct/2015:00:28:40 +0100] \"POST /join_form HTTP/1.0\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n107.158.89.247 - - [29/Oct/2015:00:28:41 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.0\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n107.158.89.247 - - [29/Oct/2015:00:28:43 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n222.141.109.17 - - [29/Oct/2015:00:34:26 +0100] \"GET /places HTTP/1.0\" 200 19359 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n222.141.109.17 - - [29/Oct/2015:00:34:31 +0100] \"GET /join_form HTTP/1.0\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n222.141.109.17 - - [29/Oct/2015:00:34:38 +0100] \"POST /join_form HTTP/1.0\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n222.141.109.17 - - [29/Oct/2015:00:34:50 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.0\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n222.141.109.17 - - [29/Oct/2015:00:34:53 +0100] \"GET /login_form HTTP/1.0\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n222.141.109.17 - - [29/Oct/2015:00:35:22 +0100] \"POST /login_form HTTP/1.0\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n198.46.159.10 - - [29/Oct/2015:00:40:48 +0100] \"GET /join_form HTTP/1.0\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n198.46.159.10 - - [29/Oct/2015:00:40:50 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n198.46.159.10 - - [29/Oct/2015:00:40:51 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n198.46.159.10 - - [29/Oct/2015:00:40:52 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n198.46.159.10 - - [29/Oct/2015:00:40:54 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n155.94.140.99 - - [29/Oct/2015:00:48:55 +0100] \"GET /join_form HTTP/1.0\" 200 12191 \"http://niels.basjes.nl\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n155.94.140.99 - - [29/Oct/2015:00:48:58 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n155.94.140.99 - - [29/Oct/2015:00:48:59 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n155.94.140.99 - - [29/Oct/2015:00:49:00 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n155.94.140.99 - - [29/Oct/2015:00:49:01 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n124.206.167.250 - - [29/Oct/2015:01:12:37 +0100] \"GET / HTTP/1.0\" 200 20478 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n124.206.167.250 - - [29/Oct/2015:01:12:47 +0100] \"GET /join_form HTTP/1.0\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n124.206.167.250 - - [29/Oct/2015:01:12:53 +0100] \"POST /join_form HTTP/1.0\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n124.206.167.250 - - [29/Oct/2015:01:13:00 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.0\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n124.206.167.250 - - [29/Oct/2015:01:13:33 +0100] \"GET /login_form HTTP/1.0\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n117.177.243.42 - - [29/Oct/2015:01:14:56 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n117.177.243.42 - - [29/Oct/2015:01:14:59 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n31.187.79.201 - - [29/Oct/2015:01:19:50 +0100] \"GET / HTTP/1.0\" 200 20478 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n31.187.79.201 - - [29/Oct/2015:01:19:52 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n31.187.79.201 - - [29/Oct/2015:01:19:53 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n31.187.79.201 - - [29/Oct/2015:01:19:54 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n31.187.79.201 - - [29/Oct/2015:01:19:55 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n31.187.79.201 - - [29/Oct/2015:01:19:56 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n192.3.106.196 - - [29/Oct/2015:01:24:33 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36444 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n192.3.106.196 - - [29/Oct/2015:01:24:34 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n192.3.106.196 - - [29/Oct/2015:01:24:35 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n192.3.106.196 - - [29/Oct/2015:01:24:36 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n192.3.106.196 - - [29/Oct/2015:01:24:37 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n192.3.106.196 - - [29/Oct/2015:01:24:38 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n23.94.119.234 - - [29/Oct/2015:01:31:27 +0100] \"GET /join_form HTTP/1.0\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n23.94.119.234 - - [29/Oct/2015:01:31:28 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n221.214.208.226 - - [29/Oct/2015:02:05:42 +0100] \"GET /places HTTP/1.1\" 200 12878 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n221.214.208.226 - - [29/Oct/2015:02:07:45 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n221.214.208.226 - - [29/Oct/2015:02:07:48 +0100] \"POST /join_form HTTP/1.1\" 302 10322 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n117.169.6.110 - - [29/Oct/2015:02:09:06 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form&last_visit:date=2015%2F10%2F29+02%3A13%3A13.091+GMT%2B1&prev_visit:date=2015%2F10%2F29+02%3A13%3A13.093+GMT%2B1&came_from_prefs=&fullname=Bonnie+Haddon&username=BonnieHadd&email=gil%40b.most-wanted-stuff.com&form.button.Register=Register&form.submitted=1 HTTP/1.1\" 200 19101 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n107.173.193.178 - - [29/Oct/2015:02:11:08 +0100] \"GET /join_form HTTP/1.0\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n107.173.193.178 - - [29/Oct/2015:02:11:08 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n107.173.193.178 - - [29/Oct/2015:02:11:09 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n107.173.193.178 - - [29/Oct/2015:02:11:10 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n31.187.79.201 - - [29/Oct/2015:02:15:22 +0100] \"GET / HTTP/1.0\" 200 20478 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n31.187.79.201 - - [29/Oct/2015:02:15:23 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n31.187.79.201 - - [29/Oct/2015:02:15:24 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n31.187.79.201 - - [29/Oct/2015:02:15:24 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n31.187.79.201 - - [29/Oct/2015:02:15:24 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n31.187.79.201 - - [29/Oct/2015:02:15:25 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n192.99.244.139 - - [29/Oct/2015:02:16:53 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36444 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.99.244.139 - - [29/Oct/2015:02:16:54 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.99.244.139 - - [29/Oct/2015:02:16:55 +0100] \"POST /join_form HTTP/1.1\" 302 9245 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.99.244.139 - - [29/Oct/2015:02:16:56 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.99.244.139 - - [29/Oct/2015:02:16:57 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.99.244.139 - - [29/Oct/2015:02:16:57 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n162.252.172.138 - - [29/Oct/2015:02:31:36 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36444 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n162.252.172.138 - - [29/Oct/2015:02:31:38 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n162.252.172.138 - - [29/Oct/2015:02:31:39 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n162.252.172.138 - - [29/Oct/2015:02:31:41 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n162.252.172.138 - - [29/Oct/2015:02:31:42 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n162.252.172.138 - - [29/Oct/2015:02:31:43 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n89.42.237.71 - - [29/Oct/2015:02:36:33 +0100] \"GET / HTTP/1.0\" 200 20478 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n89.42.237.71 - - [29/Oct/2015:02:36:35 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n89.42.237.71 - - [29/Oct/2015:02:36:36 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n89.42.237.71 - - [29/Oct/2015:02:36:37 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n89.42.237.71 - - [29/Oct/2015:02:36:39 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n89.42.237.71 - - [29/Oct/2015:02:36:40 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n195.154.46.135 - - [29/Oct/2015:03:05:09 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n31.220.30.157 - - [29/Oct/2015:03:36:46 +0100] \"GET / HTTP/1.0\" 200 20478 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n31.220.30.157 - - [29/Oct/2015:03:36:47 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n31.220.30.157 - - [29/Oct/2015:03:36:47 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n31.220.30.157 - - [29/Oct/2015:03:36:48 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n31.220.30.157 - - [29/Oct/2015:03:36:48 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n31.220.30.157 - - [29/Oct/2015:03:36:49 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n23.232.137.128 - - [29/Oct/2015:03:45:27 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.0\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n23.232.137.128 - - [29/Oct/2015:03:45:28 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n31.187.79.108 - - [29/Oct/2015:04:01:04 +0100] \"GET / HTTP/1.0\" 200 20478 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n31.187.79.108 - - [29/Oct/2015:04:01:05 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n31.187.79.108 - - [29/Oct/2015:04:01:06 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n31.187.79.108 - - [29/Oct/2015:04:01:06 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n31.187.79.108 - - [29/Oct/2015:04:01:07 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n31.187.79.108 - - [29/Oct/2015:04:01:07 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n162.253.52.250 - - [29/Oct/2015:04:07:05 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36444 \"-\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n162.253.52.250 - - [29/Oct/2015:04:07:06 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"-\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n162.253.52.250 - - [29/Oct/2015:04:07:07 +0100] \"POST /join_form HTTP/1.1\" 302 9245 \"-\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n162.253.52.250 - - [29/Oct/2015:04:07:08 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"-\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n162.253.52.250 - - [29/Oct/2015:04:07:08 +0100] \"GET /login_form HTTP/1.1\" 200 10505 \"-\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n162.253.52.250 - - [29/Oct/2015:04:07:09 +0100] \"POST /login_form HTTP/1.1\" 200 16772 \"-\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n172.245.82.98 - - [29/Oct/2015:04:29:42 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.0\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n172.245.82.98 - - [29/Oct/2015:04:29:43 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n148.251.65.34 - - [29/Oct/2015:04:31:27 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36444 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n158.222.10.95 - - [29/Oct/2015:04:31:28 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n167.160.110.44 - - [29/Oct/2015:04:31:30 +0100] \"POST /join_form HTTP/1.0\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n167.160.110.44 - - [29/Oct/2015:04:31:31 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.0\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n167.160.110.44 - - [29/Oct/2015:04:31:32 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n167.160.110.44 - - [29/Oct/2015:04:31:33 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n195.154.46.135 - - [29/Oct/2015:04:34:44 +0100] \"GET /open-source HTTP/1.1\" 200 17529 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.3.251.69 - - [29/Oct/2015:04:34:45 +0100] \"GET /join_form HTTP/1.0\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.3.251.69 - - [29/Oct/2015:04:34:47 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.3.251.69 - - [29/Oct/2015:04:34:49 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n195.154.46.135 - - [29/Oct/2015:04:44:36 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.1\" 200 24323 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n198.12.82.152 - - [29/Oct/2015:04:44:37 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n198.12.82.152 - - [29/Oct/2015:04:44:38 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n198.12.82.152 - - [29/Oct/2015:04:44:39 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n104.247.99.226 - - [29/Oct/2015:04:51:04 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.0\" 200 24323 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n104.247.99.226 - - [29/Oct/2015:04:51:07 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n104.247.99.226 - - [29/Oct/2015:04:51:09 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n104.247.99.226 - - [29/Oct/2015:04:51:11 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n104.247.99.226 - - [29/Oct/2015:04:51:14 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n104.247.99.226 - - [29/Oct/2015:04:51:16 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n195.154.46.135 - - [29/Oct/2015:05:05:19 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.3.251.47 - - [29/Oct/2015:05:05:21 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.0\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n155.94.217.216 - - [29/Oct/2015:05:16:54 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36444 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n155.94.217.216 - - [29/Oct/2015:05:16:56 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n155.94.217.216 - - [29/Oct/2015:05:16:57 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n155.94.217.216 - - [29/Oct/2015:05:16:58 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n155.94.217.216 - - [29/Oct/2015:05:16:59 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n155.94.217.216 - - [29/Oct/2015:05:17:00 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n192.99.244.139 - - [29/Oct/2015:05:17:00 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36444 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.99.244.139 - - [29/Oct/2015:05:17:02 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.99.244.139 - - [29/Oct/2015:05:17:03 +0100] \"POST /join_form HTTP/1.1\" 302 9245 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.99.244.139 - - [29/Oct/2015:05:17:04 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.99.244.139 - - [29/Oct/2015:05:17:04 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.99.244.139 - - [29/Oct/2015:05:17:05 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n195.154.46.135 - - [29/Oct/2015:05:24:29 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.3.251.112 - - [29/Oct/2015:05:24:30 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.0\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n195.154.46.135 - - [29/Oct/2015:05:26:16 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n23.247.146.31 - - [29/Oct/2015:05:26:17 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.0\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n23.94.66.103 - - [29/Oct/2015:05:27:10 +0100] \"GET /linux/installing-my-new-server/networking HTTP/1.0\" 200 27452 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n23.94.66.103 - - [29/Oct/2015:05:27:12 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n23.94.66.103 - - [29/Oct/2015:05:27:13 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n23.94.66.103 - - [29/Oct/2015:05:27:14 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n23.94.66.103 - - [29/Oct/2015:05:27:15 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n23.94.66.103 - - [29/Oct/2015:05:27:17 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n192.99.244.139 - - [29/Oct/2015:05:27:18 +0100] \"GET /accessibility-info HTTP/1.1\" 200 20626 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n121.204.77.53 - - [29/Oct/2015:05:44:46 +0100] \"GET /linux/installing-my-new-server/networking HTTP/1.1\" 200 27452 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n121.204.77.53 - - [29/Oct/2015:05:44:48 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n121.204.77.53 - - [29/Oct/2015:05:44:49 +0100] \"POST /join_form HTTP/1.1\" 302 9245 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n121.204.77.53 - - [29/Oct/2015:05:44:50 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n121.204.77.53 - - [29/Oct/2015:05:44:51 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n121.204.77.53 - - [29/Oct/2015:05:44:53 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n154.16.72.210 - - [29/Oct/2015:06:01:52 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.0\" 200 24323 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n154.16.72.210 - - [29/Oct/2015:06:01:53 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n154.16.72.210 - - [29/Oct/2015:06:01:54 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n154.16.72.210 - - [29/Oct/2015:06:01:55 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n154.16.72.210 - - [29/Oct/2015:06:01:55 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n154.16.72.210 - - [29/Oct/2015:06:01:56 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n31.187.79.212 - - [29/Oct/2015:06:06:54 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basj.es/join_form HTTP/1.0\" 200 11713 \"http://niels.basj.es/\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n31.187.79.212 - - [29/Oct/2015:06:06:56 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basj.es/join_form HTTP/1.1\" 200 11713 \"http://niels.basj.es/\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n148.251.65.34 - - [29/Oct/2015:06:14:06 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36444 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n158.222.10.36 - - [29/Oct/2015:06:14:07 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n158.222.12.205 - - [29/Oct/2015:06:14:09 +0100] \"POST /join_form HTTP/1.0\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n172.245.225.201 - - [29/Oct/2015:06:26:06 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36444 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n172.245.225.201 - - [29/Oct/2015:06:26:08 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n172.245.225.201 - - [29/Oct/2015:06:26:09 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n172.245.225.201 - - [29/Oct/2015:06:26:10 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n172.245.225.201 - - [29/Oct/2015:06:26:11 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n172.245.225.201 - - [29/Oct/2015:06:26:12 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n195.154.216.59 - - [29/Oct/2015:06:29:00 +0100] \"GET / HTTP/1.1\" 200 20526 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n195.154.216.59 - - [29/Oct/2015:06:29:01 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n195.154.216.59 - - [29/Oct/2015:06:29:01 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n195.154.216.59 - - [29/Oct/2015:06:29:02 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n195.154.216.59 - - [29/Oct/2015:06:29:02 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n195.154.216.59 - - [29/Oct/2015:06:29:03 +0100] \"POST /login_form HTTP/1.1\" 200 18402 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n192.99.244.139 - - [29/Oct/2015:06:31:55 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36444 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n195.154.46.135 - - [29/Oct/2015:06:44:34 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n104.223.29.240 - - [29/Oct/2015:06:44:36 +0100] \"GET /login_form HTTP/1.0\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n23.247.146.31 - - [29/Oct/2015:06:44:37 +0100] \"POST /login_form HTTP/1.0\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n2001:41d0:8:f69::1 - - [29/Oct/2015:06:47:36 +0100] \"GET / HTTP/1.1\" 200 20526 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n2001:41d0:8:f69::1 - - [29/Oct/2015:06:47:37 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n2001:41d0:8:f69::1 - - [29/Oct/2015:06:47:38 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n2001:41d0:8:f69::1 - - [29/Oct/2015:06:47:38 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n2001:41d0:8:f69::1 - - [29/Oct/2015:06:47:39 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n2001:41d0:8:f69::1 - - [29/Oct/2015:06:47:39 +0100] \"POST /login_form HTTP/1.1\" 200 18402 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n89.44.18.186 - - [29/Oct/2015:06:58:55 +0100] \"GET /contact-info HTTP/1.0\" 200 15801 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n89.44.18.186 - - [29/Oct/2015:06:58:56 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n89.44.18.186 - - [29/Oct/2015:06:58:58 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n89.44.18.186 - - [29/Oct/2015:06:58:59 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n148.251.65.34 - - [29/Oct/2015:07:00:36 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36444 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n107.158.89.132 - - [29/Oct/2015:07:00:38 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n107.158.89.132 - - [29/Oct/2015:07:00:39 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n107.158.89.132 - - [29/Oct/2015:07:00:40 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n107.158.89.132 - - [29/Oct/2015:07:00:42 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n107.158.89.132 - - [29/Oct/2015:07:00:43 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n188.240.136.29 - - [29/Oct/2015:07:01:05 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.0\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n188.240.136.29 - - [29/Oct/2015:07:01:06 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n89.44.18.52 - - [29/Oct/2015:07:03:42 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.0\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n89.44.18.52 - - [29/Oct/2015:07:03:45 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n89.44.18.52 - - [29/Oct/2015:07:03:46 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n89.44.18.52 - - [29/Oct/2015:07:03:48 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20100101 Firefox/17.0\"\n119.215.70.98 - - [29/Oct/2015:07:13:54 +0100] \"GET /places HTTP/1.1\" 200 19359 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n119.215.70.98 - - [29/Oct/2015:07:13:56 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n119.215.70.98 - - [29/Oct/2015:07:13:58 +0100] \"POST /join_form HTTP/1.1\" 302 10322 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n119.215.70.98 - - [29/Oct/2015:07:14:00 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n119.215.70.98 - - [29/Oct/2015:07:14:03 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n119.215.70.98 - - [29/Oct/2015:07:14:05 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60\"\n162.252.172.138 - - [29/Oct/2015:07:20:02 +0100] \"GET / HTTP/1.1\" 200 15381 \"http://daniel_en_sander.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n162.252.172.138 - - [29/Oct/2015:07:20:04 +0100] \"GET /join_form HTTP/1.1\" 200 11652 \"http://daniel_en_sander.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n162.252.172.138 - - [29/Oct/2015:07:20:08 +0100] \"POST /join_form HTTP/1.1\" 302 9556 \"http://daniel_en_sander.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n162.252.172.138 - - [29/Oct/2015:07:20:10 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//daniel_en_sander.basjes.nl/join_form HTTP/1.1\" 403 340 \"http://daniel_en_sander.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n46.102.99.140 - - [29/Oct/2015:07:20:54 +0100] \"GET /author/nielsbasjes HTTP/1.0\" 200 15519 \"http://niels.basj.es/\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n46.102.99.140 - - [29/Oct/2015:07:20:56 +0100] \"GET /join_form HTTP/1.1\" 200 12121 \"http://niels.basj.es/\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n46.102.99.140 - - [29/Oct/2015:07:21:02 +0100] \"POST /join_form HTTP/1.1\" 302 10098 \"http://niels.basj.es/join_form\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n46.102.99.140 - - [29/Oct/2015:07:21:04 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basj.es/join_form HTTP/1.1\" 200 11713 \"http://niels.basj.es/join_form\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n46.102.99.140 - - [29/Oct/2015:07:21:06 +0100] \"GET /login_form HTTP/1.1\" 200 11544 \"http://niels.basj.es/\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n46.102.99.140 - - [29/Oct/2015:07:21:08 +0100] \"POST /login_form HTTP/1.1\" 200 18241 \"http://niels.basj.es/login_form\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n93.179.90.155 - - [29/Oct/2015:07:22:45 +0100] \"GET / HTTP/1.0\" 200 20478 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n93.179.90.155 - - [29/Oct/2015:07:22:46 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n211.25.233.226 - - [29/Oct/2015:07:22:52 +0100] \"POST /join_form HTTP/1.0\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n211.25.233.226 - - [29/Oct/2015:07:23:05 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.0\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n117.253.237.6 - - [29/Oct/2015:07:28:14 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.0\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n117.253.237.6 - - [29/Oct/2015:07:28:18 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n23.250.29.33 - - [29/Oct/2015:07:48:12 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n23.250.29.33 - - [29/Oct/2015:07:48:13 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n23.250.29.33 - - [29/Oct/2015:07:48:14 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n23.250.29.33 - - [29/Oct/2015:07:48:15 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n23.250.29.33 - - [29/Oct/2015:07:48:16 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n112.14.108.18 - - [29/Oct/2015:08:03:57 +0100] \"GET / HTTP/1.1\" 200 20478 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n148.251.65.34 - - [29/Oct/2015:08:06:31 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36444 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n66.248.207.252 - - [29/Oct/2015:08:06:33 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n66.248.207.252 - - [29/Oct/2015:08:06:35 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n148.251.65.34 - - [29/Oct/2015:08:11:23 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.161.192.252 - - [29/Oct/2015:08:11:24 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.0\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n94.23.33.25 - - [29/Oct/2015:08:27:04 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36444 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n167.160.110.170 - - [29/Oct/2015:08:27:06 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n167.160.110.170 - - [29/Oct/2015:08:27:07 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n167.160.110.170 - - [29/Oct/2015:08:27:08 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n167.160.110.170 - - [29/Oct/2015:08:27:09 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n167.160.110.170 - - [29/Oct/2015:08:27:10 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n195.154.46.135 - - [29/Oct/2015:08:35:45 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n23.254.164.173 - - [29/Oct/2015:08:54:28 +0100] \"GET /linux/installing-my-new-server/voice-over-ip/RK=0/RS=tDIUMWhLOlRs5FUbKMPumYVN.f0- HTTP/1.1\" 404 12146 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.52 Safari/537.36\"\n23.254.164.173 - - [29/Oct/2015:08:54:30 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/linux/installing-my-new-server/voice-over-ip/RK=0/RS=tDIUMWhLOlRs5FUbKMPumYVN.f0-\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.52 Safari/537.36\"\n23.254.164.173 - - [29/Oct/2015:08:54:31 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.52 Safari/537.36\"\n23.254.164.173 - - [29/Oct/2015:08:54:32 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.52 Safari/537.36\"\n23.254.164.173 - - [29/Oct/2015:08:54:33 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.52 Safari/537.36\"\n23.254.164.173 - - [29/Oct/2015:08:54:34 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.52 Safari/537.36\"\n146.185.203.16 - - [29/Oct/2015:09:22:20 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.0\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n146.185.203.16 - - [29/Oct/2015:09:22:21 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n146.185.203.16 - - [29/Oct/2015:09:22:22 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n89.42.237.71 - - [29/Oct/2015:09:41:34 +0100] \"GET / HTTP/1.0\" 200 20478 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n89.42.237.71 - - [29/Oct/2015:09:41:36 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n89.42.237.71 - - [29/Oct/2015:09:41:37 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n89.42.237.71 - - [29/Oct/2015:09:41:39 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n89.42.237.71 - - [29/Oct/2015:09:41:40 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n89.42.237.71 - - [29/Oct/2015:09:41:40 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n2400:8900::f03c:91ff:fe50:5089 - - [29/Oct/2015:09:59:21 +0100] \"GET / HTTP/1.1\" 200 20478 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n2400:8900::f03c:91ff:fe50:5089 - - [29/Oct/2015:09:59:28 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n2400:8900::f03c:91ff:fe50:5089 - - [29/Oct/2015:09:59:29 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n2400:8900::f03c:91ff:fe50:5089 - - [29/Oct/2015:09:59:31 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n2400:8900::f03c:91ff:fe50:5089 - - [29/Oct/2015:09:59:33 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n2400:8900::f03c:91ff:fe50:5089 - - [29/Oct/2015:09:59:34 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n61.92.43.177 - - [29/Oct/2015:10:35:46 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36444 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n61.92.43.177 - - [29/Oct/2015:10:35:49 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n61.92.43.177 - - [29/Oct/2015:10:35:51 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n61.92.43.177 - - [29/Oct/2015:10:35:53 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n61.92.43.177 - - [29/Oct/2015:10:35:54 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n61.92.43.177 - - [29/Oct/2015:10:35:56 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.161.195.150 - - [29/Oct/2015:10:42:00 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.161.195.150 - - [29/Oct/2015:10:42:01 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n180.180.119.9 - - [29/Oct/2015:10:48:26 +0100] \"GET /open-source HTTP/1.1\" 200 17529 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n180.180.119.9 - - [29/Oct/2015:10:48:29 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n180.180.119.9 - - [29/Oct/2015:10:48:30 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n180.180.119.9 - - [29/Oct/2015:10:48:32 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n180.180.119.9 - - [29/Oct/2015:10:48:34 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n180.180.119.9 - - [29/Oct/2015:10:48:35 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n5.175.205.75 - - [29/Oct/2015:10:57:26 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//fbsafetyguide.bravesites.com HTTP/1.0\" 200 11743 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n5.175.205.75 - - [29/Oct/2015:10:57:27 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/acl_users/credentials_cookie_auth/require_login?came_from=http%3A//fbsafetyguide.bravesites.com\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n5.175.205.75 - - [29/Oct/2015:10:57:28 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.3.243.125 - - [29/Oct/2015:11:10:28 +0100] \"GET /linux/installing-my-new-server/networking HTTP/1.0\" 200 27452 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.3.243.125 - - [29/Oct/2015:11:10:30 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.3.243.125 - - [29/Oct/2015:11:10:31 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.3.243.125 - - [29/Oct/2015:11:10:32 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.3.243.125 - - [29/Oct/2015:11:10:33 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.3.243.125 - - [29/Oct/2015:11:10:34 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n23.226.70.214 - - [29/Oct/2015:11:14:08 +0100] \"GET /places HTTP/1.1\" 200 19359 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n23.226.70.214 - - [29/Oct/2015:11:14:09 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n23.226.70.214 - - [29/Oct/2015:11:14:10 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n23.226.70.214 - - [29/Oct/2015:11:14:11 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n23.226.70.214 - - [29/Oct/2015:11:14:12 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n23.226.70.214 - - [29/Oct/2015:11:14:13 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n154.122.40.141 - - [29/Oct/2015:11:29:09 +0100] \"GET /open-source HTTP/1.1\" 200 17529 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n154.122.40.141 - - [29/Oct/2015:11:29:21 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n154.122.40.141 - - [29/Oct/2015:11:29:26 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n154.122.40.141 - - [29/Oct/2015:11:29:28 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n172.245.226.147 - - [29/Oct/2015:11:30:56 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n172.245.226.147 - - [29/Oct/2015:11:30:57 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n172.245.226.147 - - [29/Oct/2015:11:30:58 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n172.245.226.147 - - [29/Oct/2015:11:30:59 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n172.245.226.147 - - [29/Oct/2015:11:31:00 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n107.173.243.62 - - [29/Oct/2015:11:31:36 +0100] \"GET /join_form HTTP/1.0\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n107.173.243.62 - - [29/Oct/2015:11:31:37 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n107.173.243.62 - - [29/Oct/2015:11:31:38 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n107.173.243.62 - - [29/Oct/2015:11:31:39 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n107.173.243.62 - - [29/Oct/2015:11:31:40 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n180.180.119.9 - - [29/Oct/2015:11:41:04 +0100] \"GET /open-source HTTP/1.1\" 200 17529 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n180.180.119.9 - - [29/Oct/2015:11:41:06 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n180.180.119.9 - - [29/Oct/2015:11:41:11 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n180.180.119.9 - - [29/Oct/2015:11:41:13 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n180.180.119.9 - - [29/Oct/2015:11:41:18 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n180.180.119.9 - - [29/Oct/2015:11:41:20 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n158.222.12.158 - - [29/Oct/2015:11:41:50 +0100] \"GET /open-source HTTP/1.0\" 200 17529 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n158.222.5.244 - - [29/Oct/2015:11:41:52 +0100] \"GET /join_form HTTP/1.0\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n158.222.5.244 - - [29/Oct/2015:11:41:53 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n158.222.5.244 - - [29/Oct/2015:11:41:54 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.77.254.250 - - [29/Oct/2015:11:42:07 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.0\" 200 36444 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.77.254.250 - - [29/Oct/2015:11:42:08 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.77.254.250 - - [29/Oct/2015:11:42:10 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.77.254.250 - - [29/Oct/2015:11:42:11 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.77.254.250 - - [29/Oct/2015:11:42:12 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.77.254.250 - - [29/Oct/2015:11:42:13 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n60.12.11.39 - - [29/Oct/2015:11:48:59 +0100] \"GET /places HTTP/1.1\" 200 19359 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n60.12.11.39 - - [29/Oct/2015:11:49:11 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n60.12.11.39 - - [29/Oct/2015:11:49:17 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n60.12.11.39 - - [29/Oct/2015:11:49:24 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n60.12.11.39 - - [29/Oct/2015:11:49:30 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n60.12.11.39 - - [29/Oct/2015:11:49:35 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n198.52.212.18 - - [29/Oct/2015:12:04:07 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n198.52.212.18 - - [29/Oct/2015:12:04:09 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n198.52.212.18 - - [29/Oct/2015:12:04:10 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n198.52.212.18 - - [29/Oct/2015:12:04:11 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n198.52.212.18 - - [29/Oct/2015:12:04:12 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n154.122.40.141 - - [29/Oct/2015:12:06:22 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n154.122.40.141 - - [29/Oct/2015:12:06:28 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n154.122.40.141 - - [29/Oct/2015:12:06:36 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n31.187.79.108 - - [29/Oct/2015:12:20:17 +0100] \"GET / HTTP/1.0\" 200 20478 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n31.187.79.108 - - [29/Oct/2015:12:20:18 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n31.187.79.108 - - [29/Oct/2015:12:20:18 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n31.187.79.108 - - [29/Oct/2015:12:20:19 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n31.187.79.108 - - [29/Oct/2015:12:20:19 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n31.187.79.108 - - [29/Oct/2015:12:20:20 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n23.94.82.231 - - [29/Oct/2015:12:20:41 +0100] \"GET / HTTP/1.0\" 200 20478 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n23.94.82.231 - - [29/Oct/2015:12:20:42 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n23.94.82.231 - - [29/Oct/2015:12:20:44 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n23.94.82.231 - - [29/Oct/2015:12:20:47 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n23.94.82.231 - - [29/Oct/2015:12:20:49 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n23.94.82.231 - - [29/Oct/2015:12:20:51 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.119.149.217 - - [29/Oct/2015:12:27:11 +0100] \"GET / HTTP/1.1\" 200 20478 \"http://niels.basjes.nl\" \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.99 Safari/537.36\"\n192.119.149.217 - - [29/Oct/2015:12:27:13 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl\" \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.99 Safari/537.36\"\n192.119.149.217 - - [29/Oct/2015:12:27:14 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.99 Safari/537.36\"\n192.119.149.217 - - [29/Oct/2015:12:27:15 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.99 Safari/537.36\"\n192.119.149.217 - - [29/Oct/2015:12:27:15 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.99 Safari/537.36\"\n192.119.149.217 - - [29/Oct/2015:12:27:16 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.99 Safari/537.36\"\n216.158.196.24 - - [29/Oct/2015:12:35:18 +0100] \"GET /open-source HTTP/1.0\" 200 17529 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n216.158.196.24 - - [29/Oct/2015:12:35:21 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n216.158.196.24 - - [29/Oct/2015:12:35:24 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n216.158.196.24 - - [29/Oct/2015:12:35:26 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n158.222.12.31 - - [29/Oct/2015:12:37:41 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.0\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n216.158.196.24 - - [29/Oct/2015:12:37:53 +0100] \"GET /login_form HTTP/1.0\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n216.158.196.24 - - [29/Oct/2015:12:37:55 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n216.158.196.24 - - [29/Oct/2015:12:42:39 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.0\" 200 24323 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n216.158.197.14 - - [29/Oct/2015:12:42:52 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n216.158.197.14 - - [29/Oct/2015:12:42:54 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n216.158.197.14 - - [29/Oct/2015:12:42:55 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n158.222.12.76 - - [29/Oct/2015:12:47:53 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.0\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n216.158.201.102 - - [29/Oct/2015:12:47:55 +0100] \"GET /login_form HTTP/1.0\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n216.158.201.102 - - [29/Oct/2015:12:47:57 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n1.0.184.119 - - [29/Oct/2015:12:50:34 +0100] \"GET /open-source HTTP/1.1\" 200 17529 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n1.0.184.119 - - [29/Oct/2015:12:50:36 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n1.0.184.119 - - [29/Oct/2015:12:50:38 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n1.0.184.119 - - [29/Oct/2015:12:50:39 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n1.0.184.119 - - [29/Oct/2015:12:50:41 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n1.0.184.119 - - [29/Oct/2015:12:50:43 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n154.122.40.141 - - [29/Oct/2015:12:52:44 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n154.122.40.141 - - [29/Oct/2015:12:52:47 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n154.122.40.141 - - [29/Oct/2015:12:52:54 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n154.122.40.141 - - [29/Oct/2015:12:52:55 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n104.168.75.182 - - [29/Oct/2015:13:01:27 +0100] \"GET /join_form HTTP/1.0\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n104.168.75.182 - - [29/Oct/2015:13:01:28 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n104.168.75.182 - - [29/Oct/2015:13:01:29 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n104.168.75.182 - - [29/Oct/2015:13:01:30 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n104.168.75.182 - - [29/Oct/2015:13:01:31 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n154.122.40.141 - - [29/Oct/2015:13:08:55 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n154.122.40.141 - - [29/Oct/2015:13:09:13 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n1.0.184.157 - - [29/Oct/2015:13:10:37 +0100] \"GET /open-source HTTP/1.1\" 200 17529 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n1.0.184.157 - - [29/Oct/2015:13:10:39 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n1.0.184.157 - - [29/Oct/2015:13:10:41 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n1.0.184.157 - - [29/Oct/2015:13:10:43 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n1.0.184.157 - - [29/Oct/2015:13:10:45 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n1.0.184.157 - - [29/Oct/2015:13:10:47 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n154.122.40.141 - - [29/Oct/2015:13:12:13 +0100] \"GET /search? HTTP/1.1\" 200 10693 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n154.122.40.141 - - [29/Oct/2015:13:12:34 +0100] \"GET /login_form HTTP/1.1\" 200 11627 \"http://niels.basjes.nl/search?\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n154.122.40.141 - - [29/Oct/2015:13:12:35 +0100] \"POST /login_form HTTP/1.1\" 200 18361 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n155.94.166.16 - - [29/Oct/2015:13:30:42 +0100] \"GET / HTTP/1.0\" 200 20478 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n155.94.166.16 - - [29/Oct/2015:13:30:48 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n155.94.166.16 - - [29/Oct/2015:13:30:52 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n155.94.166.16 - - [29/Oct/2015:13:30:55 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n155.94.166.16 - - [29/Oct/2015:13:31:00 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n155.94.166.16 - - [29/Oct/2015:13:31:04 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.161.167.72 - - [29/Oct/2015:13:57:10 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.161.167.72 - - [29/Oct/2015:13:57:11 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.161.167.72 - - [29/Oct/2015:13:57:12 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.161.167.72 - - [29/Oct/2015:13:57:14 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.161.167.72 - - [29/Oct/2015:13:57:15 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n91.108.180.7 - - [29/Oct/2015:14:07:15 +0100] \"GET /join_form HTTP/1.0\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n91.108.180.7 - - [29/Oct/2015:14:07:16 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n91.108.180.7 - - [29/Oct/2015:14:07:16 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n91.108.180.7 - - [29/Oct/2015:14:07:17 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n91.108.180.7 - - [29/Oct/2015:14:07:18 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n165.231.90.152 - - [29/Oct/2015:14:07:50 +0100] \"GET /join_form HTTP/1.0\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n165.231.90.152 - - [29/Oct/2015:14:07:51 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n165.231.90.152 - - [29/Oct/2015:14:07:52 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n165.231.90.152 - - [29/Oct/2015:14:07:54 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n165.231.90.152 - - [29/Oct/2015:14:07:55 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n91.139.172.39 - - [29/Oct/2015:14:10:44 +0100] \"GET / HTTP/1.1\" 200 15381 \"http://daniel_en_sander.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n91.139.172.39 - - [29/Oct/2015:14:10:45 +0100] \"GET /join_form HTTP/1.1\" 200 11652 \"http://daniel_en_sander.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n91.139.172.39 - - [29/Oct/2015:14:10:45 +0100] \"POST /join_form HTTP/1.1\" 302 9556 \"http://daniel_en_sander.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n91.139.172.39 - - [29/Oct/2015:14:10:46 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//daniel_en_sander.basjes.nl/join_form HTTP/1.1\" 403 340 \"http://daniel_en_sander.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n212.129.17.73 - - [29/Oct/2015:14:15:15 +0100] \"GET /places HTTP/1.1\" 200 19359 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n180.250.147.37 - - [29/Oct/2015:14:15:18 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n46.102.99.131 - - [29/Oct/2015:14:35:30 +0100] \"GET /join_form HTTP/1.0\" 200 12121 \"http://niels.basj.es/\" \"Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n46.102.99.131 - - [29/Oct/2015:14:35:34 +0100] \"GET /join_form HTTP/1.1\" 200 12121 \"http://niels.basj.es/\" \"Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n46.102.99.131 - - [29/Oct/2015:14:35:35 +0100] \"POST /join_form HTTP/1.1\" 302 10098 \"http://niels.basj.es/join_form\" \"Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n46.102.99.131 - - [29/Oct/2015:14:35:35 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basj.es/join_form HTTP/1.1\" 200 11713 \"http://niels.basj.es/join_form\" \"Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n46.102.99.131 - - [29/Oct/2015:14:35:36 +0100] \"GET /login_form HTTP/1.1\" 200 11544 \"http://niels.basj.es/\" \"Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n46.102.99.131 - - [29/Oct/2015:14:35:39 +0100] \"POST /login_form HTTP/1.1\" 200 18241 \"http://niels.basj.es/login_form\" \"Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n204.44.85.215 - - [29/Oct/2015:14:48:05 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36444 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.99 Safari/537.36\"\n204.44.85.215 - - [29/Oct/2015:14:48:07 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.99 Safari/537.36\"\n204.44.85.215 - - [29/Oct/2015:14:48:08 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.99 Safari/537.36\"\n204.44.85.215 - - [29/Oct/2015:14:48:09 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.99 Safari/537.36\"\n204.44.85.215 - - [29/Oct/2015:14:48:10 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.99 Safari/537.36\"\n204.44.85.215 - - [29/Oct/2015:14:48:12 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.99 Safari/537.36\"\n158.222.5.79 - - [29/Oct/2015:15:16:56 +0100] \"GET /splittable-gzip HTTP/1.0\" 200 27764 \"http://niels.basj.es/\" \"Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n158.222.5.79 - - [29/Oct/2015:15:16:57 +0100] \"GET /join_form HTTP/1.1\" 200 12121 \"http://niels.basj.es/\" \"Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n158.222.5.79 - - [29/Oct/2015:15:16:59 +0100] \"POST /join_form HTTP/1.1\" 302 10098 \"http://niels.basj.es/join_form\" \"Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n23.250.29.33 - - [29/Oct/2015:15:22:29 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n23.250.29.33 - - [29/Oct/2015:15:22:30 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n23.250.29.33 - - [29/Oct/2015:15:22:31 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n23.250.29.33 - - [29/Oct/2015:15:22:32 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n23.250.29.33 - - [29/Oct/2015:15:22:33 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n216.158.196.211 - - [29/Oct/2015:15:24:18 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36444 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.99 Safari/537.36\"\n216.158.196.211 - - [29/Oct/2015:15:24:21 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.99 Safari/537.36\"\n216.158.196.211 - - [29/Oct/2015:15:24:22 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.99 Safari/537.36\"\n216.158.196.211 - - [29/Oct/2015:15:24:23 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.99 Safari/537.36\"\n216.158.196.211 - - [29/Oct/2015:15:24:24 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.99 Safari/537.36\"\n216.158.196.211 - - [29/Oct/2015:15:24:26 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.99 Safari/537.36\"\n172.245.226.147 - - [29/Oct/2015:15:46:26 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n172.245.226.147 - - [29/Oct/2015:15:46:27 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n172.245.226.147 - - [29/Oct/2015:15:46:28 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n172.245.226.147 - - [29/Oct/2015:15:46:29 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n172.245.226.147 - - [29/Oct/2015:15:46:30 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n198.12.82.164 - - [29/Oct/2015:15:47:36 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36444 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.99 Safari/537.36\"\n198.12.82.164 - - [29/Oct/2015:15:47:39 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.99 Safari/537.36\"\n198.12.82.164 - - [29/Oct/2015:15:47:40 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.99 Safari/537.36\"\n198.12.82.164 - - [29/Oct/2015:15:47:41 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.99 Safari/537.36\"\n198.12.82.164 - - [29/Oct/2015:15:47:42 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.99 Safari/537.36\"\n198.12.82.164 - - [29/Oct/2015:15:47:43 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.99 Safari/537.36\"\n198.46.232.230 - - [29/Oct/2015:15:58:25 +0100] \"GET /search?Subject%3Alist=Linux%20Installation HTTP/1.0\" 200 11549 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n198.46.232.230 - - [29/Oct/2015:15:58:27 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n198.46.232.230 - - [29/Oct/2015:15:58:28 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n198.46.232.230 - - [29/Oct/2015:15:58:29 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n198.46.232.230 - - [29/Oct/2015:15:58:32 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n198.46.232.230 - - [29/Oct/2015:15:58:33 +0100] \"POST /login_form HTTP/1.1\" 200 16862 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n107.158.89.196 - - [29/Oct/2015:16:15:54 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n107.158.89.196 - - [29/Oct/2015:16:15:55 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n107.158.89.196 - - [29/Oct/2015:16:15:57 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n107.158.89.196 - - [29/Oct/2015:16:15:58 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n107.158.89.196 - - [29/Oct/2015:16:16:00 +0100] \"POST /login_form HTTP/1.1\" 200 16862 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n1.0.186.198 - - [29/Oct/2015:16:16:36 +0100] \"GET / HTTP/1.1\" 200 20478 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n1.0.186.198 - - [29/Oct/2015:16:16:38 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.3.182.103 - - [29/Oct/2015:16:17:20 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.0\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.3.182.103 - - [29/Oct/2015:16:17:21 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n198.23.214.145 - - [29/Oct/2015:16:22:16 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n198.23.214.145 - - [29/Oct/2015:16:22:17 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n198.23.214.145 - - [29/Oct/2015:16:22:18 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n198.23.214.145 - - [29/Oct/2015:16:22:19 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n198.23.214.145 - - [29/Oct/2015:16:22:21 +0100] \"POST /login_form HTTP/1.1\" 200 16862 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n1.0.186.117 - - [29/Oct/2015:16:24:37 +0100] \"GET /open-source HTTP/1.1\" 200 17529 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n1.0.186.117 - - [29/Oct/2015:16:24:39 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n1.0.186.117 - - [29/Oct/2015:16:24:41 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n1.0.186.117 - - [29/Oct/2015:16:24:43 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n1.0.186.117 - - [29/Oct/2015:16:24:45 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n1.0.186.117 - - [29/Oct/2015:16:24:47 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n45.33.159.124 - - [29/Oct/2015:16:32:36 +0100] \"GET /places HTTP/1.0\" 200 19359 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n45.33.159.124 - - [29/Oct/2015:16:32:39 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n45.33.159.124 - - [29/Oct/2015:16:32:43 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n23.94.170.4 - - [29/Oct/2015:16:32:43 +0100] \"GET /places HTTP/1.0\" 200 19359 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n23.94.170.4 - - [29/Oct/2015:16:32:44 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n45.33.159.124 - - [29/Oct/2015:16:32:45 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n23.94.170.4 - - [29/Oct/2015:16:32:46 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n23.94.170.4 - - [29/Oct/2015:16:32:47 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n45.33.159.124 - - [29/Oct/2015:16:32:49 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n23.94.170.4 - - [29/Oct/2015:16:32:49 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n23.94.170.4 - - [29/Oct/2015:16:32:50 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n45.33.159.124 - - [29/Oct/2015:16:32:52 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n23.231.25.32 - - [29/Oct/2015:16:53:04 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.0\" 200 24323 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n23.231.25.32 - - [29/Oct/2015:16:53:07 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n23.231.25.32 - - [29/Oct/2015:16:53:08 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n23.231.25.32 - - [29/Oct/2015:16:53:09 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n23.231.25.32 - - [29/Oct/2015:16:53:11 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n23.231.25.32 - - [29/Oct/2015:16:53:12 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n216.158.196.202 - - [29/Oct/2015:17:08:15 +0100] \"GET / HTTP/1.1\" 200 20478 \"http://niels.basjes.nl\" \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.99 Safari/537.36\"\n216.158.196.202 - - [29/Oct/2015:17:08:17 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl\" \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.99 Safari/537.36\"\n216.158.196.202 - - [29/Oct/2015:17:08:18 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.99 Safari/537.36\"\n216.158.196.202 - - [29/Oct/2015:17:08:19 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.99 Safari/537.36\"\n216.158.196.202 - - [29/Oct/2015:17:08:21 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.99 Safari/537.36\"\n216.158.196.202 - - [29/Oct/2015:17:08:22 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.99 Safari/537.36\"\n23.94.125.45 - - [29/Oct/2015:17:10:43 +0100] \"GET /linux/installing-my-new-server/networking HTTP/1.0\" 200 27452 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n23.94.125.45 - - [29/Oct/2015:17:10:44 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n23.94.125.45 - - [29/Oct/2015:17:10:45 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n23.94.125.45 - - [29/Oct/2015:17:10:46 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n23.94.125.45 - - [29/Oct/2015:17:10:47 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n23.94.125.45 - - [29/Oct/2015:17:10:48 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n148.251.65.34 - - [29/Oct/2015:17:14:58 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36444 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n104.227.92.11 - - [29/Oct/2015:17:14:59 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n104.227.92.11 - - [29/Oct/2015:17:15:00 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n104.227.92.11 - - [29/Oct/2015:17:15:01 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n104.227.92.11 - - [29/Oct/2015:17:15:03 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n104.227.92.11 - - [29/Oct/2015:17:15:04 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n149.202.26.94 - - [29/Oct/2015:17:17:42 +0100] \"GET /places HTTP/1.1\" 200 19359 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n149.202.26.94 - - [29/Oct/2015:17:17:43 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n149.202.26.94 - - [29/Oct/2015:17:17:45 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n149.202.26.94 - - [29/Oct/2015:17:17:46 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n149.202.26.94 - - [29/Oct/2015:17:17:49 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n149.202.26.94 - - [29/Oct/2015:17:17:50 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n167.160.127.104 - - [29/Oct/2015:17:24:27 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n167.160.127.104 - - [29/Oct/2015:17:24:28 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.99.244.139 - - [29/Oct/2015:17:25:31 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36444 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.99.244.139 - - [29/Oct/2015:17:25:32 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.99.244.139 - - [29/Oct/2015:17:25:33 +0100] \"POST /join_form HTTP/1.1\" 302 9245 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.99.244.139 - - [29/Oct/2015:17:25:34 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.99.244.139 - - [29/Oct/2015:17:25:34 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.99.244.139 - - [29/Oct/2015:17:25:35 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n221.176.14.78 - - [29/Oct/2015:17:49:51 +0100] \"GET /places HTTP/1.0\" 200 19359 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n221.176.14.78 - - [29/Oct/2015:17:49:55 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n221.176.14.78 - - [29/Oct/2015:17:49:59 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n221.176.14.78 - - [29/Oct/2015:17:50:01 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n221.176.14.78 - - [29/Oct/2015:17:50:03 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n221.176.14.78 - - [29/Oct/2015:17:50:06 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0\"\n88.150.236.21 - - [29/Oct/2015:18:13:46 +0100] \"GET /join_form HTTP/1.0\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n88.150.236.21 - - [29/Oct/2015:18:13:47 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n88.150.236.21 - - [29/Oct/2015:18:13:47 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n88.150.236.21 - - [29/Oct/2015:18:13:47 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n88.150.236.21 - - [29/Oct/2015:18:13:48 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n162.252.172.138 - - [29/Oct/2015:18:17:17 +0100] \"GET / HTTP/1.1\" 200 15381 \"http://daniel_en_sander.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n162.252.172.138 - - [29/Oct/2015:18:17:19 +0100] \"GET /join_form HTTP/1.1\" 200 11652 \"http://daniel_en_sander.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n162.252.172.138 - - [29/Oct/2015:18:17:21 +0100] \"POST /join_form HTTP/1.1\" 302 9556 \"http://daniel_en_sander.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n162.252.172.138 - - [29/Oct/2015:18:17:22 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//daniel_en_sander.basjes.nl/join_form HTTP/1.1\" 403 340 \"http://daniel_en_sander.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n167.160.127.116 - - [29/Oct/2015:18:20:41 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n167.160.127.116 - - [29/Oct/2015:18:20:42 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n50.2.211.101 - - [29/Oct/2015:18:22:36 +0100] \"GET /join_form HTTP/1.0\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n50.2.211.101 - - [29/Oct/2015:18:22:37 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n50.2.211.101 - - [29/Oct/2015:18:22:38 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n50.2.211.101 - - [29/Oct/2015:18:22:39 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n50.2.211.101 - - [29/Oct/2015:18:22:40 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n180.180.107.129 - - [29/Oct/2015:18:39:23 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.1\" 200 24323 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n180.180.107.129 - - [29/Oct/2015:18:39:26 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n180.180.107.129 - - [29/Oct/2015:18:39:27 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n180.180.107.129 - - [29/Oct/2015:18:39:29 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n180.180.107.129 - - [29/Oct/2015:18:39:31 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n180.180.107.129 - - [29/Oct/2015:18:39:36 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n155.94.138.198 - - [29/Oct/2015:19:09:47 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n155.94.138.198 - - [29/Oct/2015:19:09:49 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n155.94.138.198 - - [29/Oct/2015:19:09:53 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n155.94.138.198 - - [29/Oct/2015:19:09:55 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n155.94.138.198 - - [29/Oct/2015:19:09:57 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n46.102.99.141 - - [29/Oct/2015:19:25:02 +0100] \"GET /join_form HTTP/1.0\" 200 12121 \"http://niels.basj.es/\" \"Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n46.102.99.141 - - [29/Oct/2015:19:25:03 +0100] \"GET /join_form HTTP/1.1\" 200 12121 \"http://niels.basj.es/\" \"Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n46.102.99.141 - - [29/Oct/2015:19:25:06 +0100] \"POST /join_form HTTP/1.1\" 302 10098 \"http://niels.basj.es/join_form\" \"Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n46.102.99.141 - - [29/Oct/2015:19:25:07 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basj.es/join_form HTTP/1.1\" 200 11713 \"http://niels.basj.es/join_form\" \"Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n46.102.99.141 - - [29/Oct/2015:19:25:08 +0100] \"GET /login_form HTTP/1.1\" 200 11544 \"http://niels.basj.es/\" \"Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n180.180.107.129 - - [29/Oct/2015:19:36:28 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.1\" 200 24323 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n180.180.107.129 - - [29/Oct/2015:19:36:30 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n180.180.107.129 - - [29/Oct/2015:19:36:32 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n180.180.107.129 - - [29/Oct/2015:19:36:33 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n180.180.107.129 - - [29/Oct/2015:19:36:35 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n180.180.107.129 - - [29/Oct/2015:19:36:36 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n180.180.107.227 - - [29/Oct/2015:19:41:20 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.1\" 200 24323 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n180.180.107.227 - - [29/Oct/2015:19:41:22 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n180.180.107.227 - - [29/Oct/2015:19:41:24 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n180.180.107.227 - - [29/Oct/2015:19:41:25 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n180.180.107.227 - - [29/Oct/2015:19:41:27 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n180.180.107.227 - - [29/Oct/2015:19:41:28 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n162.252.172.138 - - [29/Oct/2015:19:44:17 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36444 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n162.252.172.138 - - [29/Oct/2015:19:44:20 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n162.252.172.138 - - [29/Oct/2015:19:44:21 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n162.252.172.138 - - [29/Oct/2015:19:44:23 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n162.252.172.138 - - [29/Oct/2015:19:44:24 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n162.252.172.138 - - [29/Oct/2015:19:44:26 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n180.180.107.129 - - [29/Oct/2015:19:50:18 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.1\" 200 24323 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n180.180.107.129 - - [29/Oct/2015:19:50:21 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n180.180.107.129 - - [29/Oct/2015:19:50:23 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n180.180.107.129 - - [29/Oct/2015:19:50:24 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n180.180.107.129 - - [29/Oct/2015:19:50:26 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n180.180.107.129 - - [29/Oct/2015:19:50:29 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n69.57.235.224 - - [29/Oct/2015:20:22:07 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n172.245.229.97 - - [29/Oct/2015:20:25:00 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n172.245.229.97 - - [29/Oct/2015:20:25:02 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n195.154.216.59 - - [29/Oct/2015:20:26:11 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.1\" 200 24323 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n195.154.216.59 - - [29/Oct/2015:20:26:12 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n195.154.216.59 - - [29/Oct/2015:20:26:12 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n195.154.216.59 - - [29/Oct/2015:20:26:13 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n195.154.216.59 - - [29/Oct/2015:20:26:13 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n195.154.216.59 - - [29/Oct/2015:20:26:14 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n172.245.82.89 - - [29/Oct/2015:20:32:41 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.0\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n172.245.82.89 - - [29/Oct/2015:20:32:42 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n172.245.82.89 - - [29/Oct/2015:20:32:44 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n172.245.82.89 - - [29/Oct/2015:20:32:45 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n172.245.82.89 - - [29/Oct/2015:20:32:48 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n172.245.82.89 - - [29/Oct/2015:20:32:50 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n162.252.172.138 - - [29/Oct/2015:20:50:43 +0100] \"GET /accessibility-info HTTP/1.1\" 200 20626 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n162.252.172.138 - - [29/Oct/2015:20:50:45 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n162.252.172.138 - - [29/Oct/2015:20:50:47 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n162.252.172.138 - - [29/Oct/2015:20:50:49 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n162.252.172.138 - - [29/Oct/2015:20:50:50 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n162.252.172.138 - - [29/Oct/2015:20:50:52 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n91.108.180.195 - - [29/Oct/2015:21:21:42 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n91.108.180.191 - - [29/Oct/2015:21:21:43 +0100] \"POST /join_form HTTP/1.0\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n75.127.10.67 - - [29/Oct/2015:21:39:04 +0100] \"GET /linux/installing-my-new-server/vmware-server HTTP/1.0\" 200 25009 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n75.127.10.67 - - [29/Oct/2015:21:39:07 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n75.127.10.67 - - [29/Oct/2015:21:39:08 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n75.127.10.67 - - [29/Oct/2015:21:39:09 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n75.127.10.67 - - [29/Oct/2015:21:39:10 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n75.127.10.67 - - [29/Oct/2015:21:39:11 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n173.232.116.147 - - [29/Oct/2015:21:42:04 +0100] \"GET / HTTP/1.0\" 200 20478 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n173.232.116.147 - - [29/Oct/2015:21:42:06 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n173.232.116.147 - - [29/Oct/2015:21:42:07 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n173.232.116.147 - - [29/Oct/2015:21:42:08 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n173.232.116.147 - - [29/Oct/2015:21:42:09 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n173.232.116.147 - - [29/Oct/2015:21:42:10 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n155.94.168.77 - - [29/Oct/2015:22:11:32 +0100] \"GET /join_form HTTP/1.0\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n155.94.168.77 - - [29/Oct/2015:22:11:37 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n155.94.168.77 - - [29/Oct/2015:22:11:43 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n155.94.168.77 - - [29/Oct/2015:22:11:48 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n155.94.168.77 - - [29/Oct/2015:22:11:53 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.99.244.139 - - [29/Oct/2015:22:30:22 +0100] \"GET /accessibility-info HTTP/1.1\" 200 20626 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.99.244.139 - - [29/Oct/2015:22:30:23 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.99.244.139 - - [29/Oct/2015:22:30:24 +0100] \"POST /join_form HTTP/1.1\" 302 10322 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.99.244.139 - - [29/Oct/2015:22:30:25 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.99.244.139 - - [29/Oct/2015:22:30:26 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.99.244.139 - - [29/Oct/2015:22:30:27 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n23.94.16.36 - - [29/Oct/2015:22:48:01 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n23.94.16.36 - - [29/Oct/2015:22:48:02 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n23.94.16.36 - - [29/Oct/2015:22:48:03 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n23.94.16.36 - - [29/Oct/2015:22:48:05 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n23.94.16.36 - - [29/Oct/2015:22:48:07 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n23.236.191.148 - - [29/Oct/2015:22:52:35 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n23.236.191.148 - - [29/Oct/2015:22:52:37 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n23.236.191.148 - - [29/Oct/2015:22:52:38 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n23.236.191.148 - - [29/Oct/2015:22:52:40 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n23.236.191.148 - - [29/Oct/2015:22:52:41 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.99.244.139 - - [29/Oct/2015:22:58:14 +0100] \"GET /accessibility-info HTTP/1.1\" 200 20626 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.99.244.139 - - [29/Oct/2015:22:58:15 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.99.244.139 - - [29/Oct/2015:22:58:16 +0100] \"POST /join_form HTTP/1.1\" 302 10322 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.99.244.139 - - [29/Oct/2015:22:58:16 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.99.244.139 - - [29/Oct/2015:22:58:17 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.99.244.139 - - [29/Oct/2015:22:58:18 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n31.220.30.157 - - [29/Oct/2015:23:05:36 +0100] \"GET / HTTP/1.0\" 200 20478 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n31.220.30.157 - - [29/Oct/2015:23:05:38 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n31.220.30.157 - - [29/Oct/2015:23:05:38 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n31.220.30.157 - - [29/Oct/2015:23:05:39 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n31.220.30.157 - - [29/Oct/2015:23:05:40 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n31.220.30.157 - - [29/Oct/2015:23:05:40 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.99.244.139 - - [29/Oct/2015:23:15:23 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36444 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.99.244.139 - - [29/Oct/2015:23:15:24 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.99.244.139 - - [29/Oct/2015:23:15:25 +0100] \"POST /join_form HTTP/1.1\" 302 9245 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.99.244.139 - - [29/Oct/2015:23:15:26 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.99.244.139 - - [29/Oct/2015:23:15:26 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.99.244.139 - - [29/Oct/2015:23:15:27 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n89.36.65.53 - - [29/Oct/2015:23:18:38 +0100] \"GET / HTTP/1.0\" 200 20478 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n89.36.65.53 - - [29/Oct/2015:23:18:39 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n89.36.65.53 - - [29/Oct/2015:23:18:39 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n89.36.65.53 - - [29/Oct/2015:23:18:40 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n89.36.65.53 - - [29/Oct/2015:23:18:41 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n89.36.65.53 - - [29/Oct/2015:23:18:42 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n94.23.33.25 - - [29/Oct/2015:23:38:45 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36444 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n107.158.89.145 - - [29/Oct/2015:23:38:47 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n107.158.89.145 - - [29/Oct/2015:23:38:49 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n107.158.89.145 - - [29/Oct/2015:23:38:50 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n107.158.89.145 - - [29/Oct/2015:23:38:52 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n107.158.89.145 - - [29/Oct/2015:23:38:53 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n5.39.5.5 - - [30/Oct/2015:00:00:59 +0100] \"GET / HTTP/1.1\" 200 17057 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n101.231.46.34 - - [30/Oct/2015:00:01:43 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n101.231.46.34 - - [30/Oct/2015:00:01:49 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n101.231.46.34 - - [30/Oct/2015:00:01:57 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n101.231.46.34 - - [30/Oct/2015:00:02:03 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n180.180.109.92 - - [30/Oct/2015:00:09:09 +0100] \"GET /open-source HTTP/1.1\" 200 17529 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n180.180.109.92 - - [30/Oct/2015:00:09:11 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n180.180.109.92 - - [30/Oct/2015:00:09:13 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n180.180.109.92 - - [30/Oct/2015:00:09:14 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n180.180.109.92 - - [30/Oct/2015:00:09:16 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n180.180.109.92 - - [30/Oct/2015:00:09:17 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n107.158.89.87 - - [30/Oct/2015:00:11:01 +0100] \"GET / HTTP/1.0\" 200 15381 \"http://daniel_en_sander.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n107.158.89.87 - - [30/Oct/2015:00:11:03 +0100] \"GET /join_form HTTP/1.1\" 200 11652 \"http://daniel_en_sander.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n107.158.89.87 - - [30/Oct/2015:00:11:04 +0100] \"POST /join_form HTTP/1.1\" 302 9556 \"http://daniel_en_sander.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n107.158.89.87 - - [30/Oct/2015:00:11:06 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//daniel_en_sander.basjes.nl/join_form HTTP/1.1\" 403 340 \"http://daniel_en_sander.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n23.95.16.212 - - [30/Oct/2015:00:30:16 +0100] \"GET /linux/installing-my-new-server/vmware-server HTTP/1.0\" 200 25009 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n23.95.16.212 - - [30/Oct/2015:00:30:19 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n23.94.105.174 - - [30/Oct/2015:00:30:21 +0100] \"POST /join_form HTTP/1.0\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n23.94.105.174 - - [30/Oct/2015:00:30:22 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.0\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n23.94.105.174 - - [30/Oct/2015:00:30:24 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n23.94.105.174 - - [30/Oct/2015:00:30:26 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n104.218.192.116 - - [30/Oct/2015:00:34:46 +0100] \"GET /linux/installing-gitlab-on-centos-6 HTTP/1.0\" 200 19422 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n104.218.192.116 - - [30/Oct/2015:00:34:47 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n104.218.192.116 - - [30/Oct/2015:00:34:48 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n104.218.192.116 - - [30/Oct/2015:00:34:49 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n104.218.192.116 - - [30/Oct/2015:00:34:50 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n104.218.192.116 - - [30/Oct/2015:00:34:53 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n94.23.33.25 - - [30/Oct/2015:00:40:35 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36444 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n192.161.167.4 - - [30/Oct/2015:00:40:37 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n192.161.167.4 - - [30/Oct/2015:00:40:38 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n192.161.167.4 - - [30/Oct/2015:00:40:40 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n192.161.167.4 - - [30/Oct/2015:00:40:41 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n192.161.167.4 - - [30/Oct/2015:00:40:43 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n5.175.177.230 - - [30/Oct/2015:00:41:46 +0100] \"GET /linux/installing-gitlab-on-centos-6 HTTP/1.0\" 200 19422 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n5.175.177.230 - - [30/Oct/2015:00:41:47 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n5.175.177.230 - - [30/Oct/2015:00:41:47 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n5.175.177.230 - - [30/Oct/2015:00:41:47 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n5.175.177.230 - - [30/Oct/2015:00:41:48 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n5.175.177.230 - - [30/Oct/2015:00:41:48 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n208.115.125.58 - - [30/Oct/2015:00:49:26 +0100] \"GET / HTTP/1.1\" 200 15381 \"http://daniel_en_sander.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n208.115.125.58 - - [30/Oct/2015:00:49:29 +0100] \"GET /join_form HTTP/1.1\" 200 11652 \"http://daniel_en_sander.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n208.115.125.58 - - [30/Oct/2015:00:49:31 +0100] \"POST /join_form HTTP/1.1\" 302 9556 \"http://daniel_en_sander.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n208.115.125.58 - - [30/Oct/2015:00:49:33 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//daniel_en_sander.basjes.nl/join_form HTTP/1.1\" 403 340 \"http://daniel_en_sander.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n5.175.176.245 - - [30/Oct/2015:00:53:08 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basj.es/join_form HTTP/1.0\" 200 11713 \"http://niels.basj.es/\" \"Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n5.175.176.245 - - [30/Oct/2015:00:53:09 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basj.es/join_form HTTP/1.1\" 200 11713 \"http://niels.basj.es/\" \"Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\"\n173.208.43.114 - - [30/Oct/2015:01:16:04 +0100] \"GET /linux/installing-my-new-server/networking HTTP/1.0\" 200 27452 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n173.208.43.114 - - [30/Oct/2015:01:16:06 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n173.208.43.114 - - [30/Oct/2015:01:16:07 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n173.208.43.114 - - [30/Oct/2015:01:16:08 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n173.208.43.114 - - [30/Oct/2015:01:16:09 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n173.208.36.49 - - [30/Oct/2015:01:50:29 +0100] \"GET / HTTP/1.0\" 200 20478 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n173.208.36.49 - - [30/Oct/2015:01:50:31 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n173.208.36.49 - - [30/Oct/2015:01:50:33 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n173.208.36.49 - - [30/Oct/2015:01:50:38 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n173.208.36.49 - - [30/Oct/2015:01:50:43 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n173.208.36.49 - - [30/Oct/2015:01:50:45 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n192.161.167.104 - - [30/Oct/2015:02:43:05 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n192.161.167.104 - - [30/Oct/2015:02:43:06 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n192.161.167.104 - - [30/Oct/2015:02:43:07 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n192.161.167.104 - - [30/Oct/2015:02:43:08 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n192.161.167.104 - - [30/Oct/2015:02:43:09 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n1.0.190.144 - - [30/Oct/2015:02:56:57 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.1\" 200 24323 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n1.0.190.144 - - [30/Oct/2015:02:57:00 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n1.0.190.144 - - [30/Oct/2015:02:57:01 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n1.0.190.144 - - [30/Oct/2015:02:57:03 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n1.0.190.144 - - [30/Oct/2015:02:57:05 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n1.0.190.144 - - [30/Oct/2015:02:57:06 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n192.161.167.104 - - [30/Oct/2015:03:05:43 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n192.161.167.104 - - [30/Oct/2015:03:05:44 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n192.161.167.104 - - [30/Oct/2015:03:05:45 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n192.161.167.104 - - [30/Oct/2015:03:05:46 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n192.161.167.104 - - [30/Oct/2015:03:05:47 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n104.128.23.188 - - [30/Oct/2015:03:09:14 +0100] \"GET / HTTP/1.0\" 200 20478 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n104.128.23.51 - - [30/Oct/2015:03:09:18 +0100] \"GET /join_form HTTP/1.0\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n104.128.23.51 - - [30/Oct/2015:03:09:19 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n155.94.221.220 - - [30/Oct/2015:03:23:01 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n155.94.221.220 - - [30/Oct/2015:03:23:03 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n155.94.221.220 - - [30/Oct/2015:03:23:04 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n155.94.221.220 - - [30/Oct/2015:03:23:05 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n155.94.221.220 - - [30/Oct/2015:03:23:07 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n45.57.167.114 - - [30/Oct/2015:03:37:38 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n45.57.167.114 - - [30/Oct/2015:03:37:39 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n45.57.167.114 - - [30/Oct/2015:03:37:40 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n45.57.167.114 - - [30/Oct/2015:03:37:41 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n45.57.167.114 - - [30/Oct/2015:03:37:42 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n158.222.2.215 - - [30/Oct/2015:04:01:39 +0100] \"GET /places HTTP/1.0\" 200 19359 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n158.222.2.215 - - [30/Oct/2015:04:01:45 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n158.222.2.215 - - [30/Oct/2015:04:01:46 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n158.222.2.215 - - [30/Oct/2015:04:01:49 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n107.172.0.248 - - [30/Oct/2015:04:09:08 +0100] \"GET /places HTTP/1.0\" 200 19359 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n107.172.0.248 - - [30/Oct/2015:04:09:10 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n107.172.0.248 - - [30/Oct/2015:04:09:11 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n107.172.0.248 - - [30/Oct/2015:04:09:12 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n107.172.0.248 - - [30/Oct/2015:04:09:13 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n107.172.0.248 - - [30/Oct/2015:04:09:14 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n204.44.114.62 - - [30/Oct/2015:04:12:13 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n204.44.114.62 - - [30/Oct/2015:04:12:14 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n204.44.114.62 - - [30/Oct/2015:04:12:15 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n204.44.114.62 - - [30/Oct/2015:04:12:16 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n204.44.114.62 - - [30/Oct/2015:04:12:17 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n1.0.181.200 - - [30/Oct/2015:04:27:13 +0100] \"GET /open-source HTTP/1.1\" 200 17529 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n1.0.181.200 - - [30/Oct/2015:04:27:16 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n1.0.181.200 - - [30/Oct/2015:04:27:17 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n1.0.181.200 - - [30/Oct/2015:04:27:19 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n1.0.181.200 - - [30/Oct/2015:04:27:21 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n1.0.181.200 - - [30/Oct/2015:04:27:24 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n1.0.181.200 - - [30/Oct/2015:04:29:10 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.1\" 200 24323 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n1.0.181.200 - - [30/Oct/2015:04:29:12 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n1.0.181.200 - - [30/Oct/2015:04:29:14 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n1.0.181.200 - - [30/Oct/2015:04:29:15 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n1.0.181.200 - - [30/Oct/2015:04:29:17 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n1.0.181.200 - - [30/Oct/2015:04:29:19 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n23.236.191.148 - - [30/Oct/2015:04:59:44 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n23.236.191.148 - - [30/Oct/2015:04:59:45 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n23.236.191.148 - - [30/Oct/2015:04:59:46 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n23.236.191.148 - - [30/Oct/2015:04:59:47 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n23.236.191.148 - - [30/Oct/2015:04:59:47 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n208.115.125.58 - - [30/Oct/2015:05:06:54 +0100] \"GET / HTTP/1.1\" 302 281 \"http://basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n208.115.125.58 - - [30/Oct/2015:05:06:56 +0100] \"GET / HTTP/1.1\" 200 20478 \"http://basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n208.115.125.58 - - [30/Oct/2015:05:06:59 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n208.115.125.58 - - [30/Oct/2015:05:07:02 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n208.115.125.58 - - [30/Oct/2015:05:07:05 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n192.161.59.143 - - [30/Oct/2015:05:26:54 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n192.161.59.143 - - [30/Oct/2015:05:26:57 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n192.161.59.143 - - [30/Oct/2015:05:26:58 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n192.161.59.143 - - [30/Oct/2015:05:26:59 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n192.161.59.143 - - [30/Oct/2015:05:27:01 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n66.248.219.252 - - [30/Oct/2015:05:34:39 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n66.248.219.252 - - [30/Oct/2015:05:34:40 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n66.248.219.252 - - [30/Oct/2015:05:34:43 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n66.248.219.252 - - [30/Oct/2015:05:34:44 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n31.220.113.224 - - [30/Oct/2015:05:51:20 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.0\" 200 24323 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n31.220.113.224 - - [30/Oct/2015:05:51:22 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n31.220.113.224 - - [30/Oct/2015:05:51:23 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.126.192.215 - - [30/Oct/2015:05:51:23 +0100] \"GET /open-source HTTP/1.0\" 200 17529 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n31.220.113.224 - - [30/Oct/2015:05:51:25 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.126.192.215 - - [30/Oct/2015:05:51:25 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n31.220.113.224 - - [30/Oct/2015:05:51:26 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n31.220.113.224 - - [30/Oct/2015:05:51:28 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n208.115.125.58 - - [30/Oct/2015:05:56:51 +0100] \"GET /linux/installing-my-new-server/vmware-server HTTP/1.1\" 200 25009 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n208.115.125.58 - - [30/Oct/2015:05:56:55 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n208.115.125.58 - - [30/Oct/2015:05:57:03 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n208.115.125.58 - - [30/Oct/2015:05:57:07 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n89.42.237.71 - - [30/Oct/2015:05:59:53 +0100] \"GET / HTTP/1.0\" 200 20478 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n89.42.237.71 - - [30/Oct/2015:05:59:54 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n89.42.237.71 - - [30/Oct/2015:05:59:55 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n89.42.237.71 - - [30/Oct/2015:05:59:56 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n89.42.237.71 - - [30/Oct/2015:05:59:57 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n89.42.237.71 - - [30/Oct/2015:05:59:57 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n180.180.69.194 - - [30/Oct/2015:06:01:31 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.1\" 200 24323 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n180.180.69.194 - - [30/Oct/2015:06:01:33 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n180.180.69.194 - - [30/Oct/2015:06:01:35 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n180.180.69.194 - - [30/Oct/2015:06:01:36 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n180.180.69.194 - - [30/Oct/2015:06:01:38 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n180.180.69.194 - - [30/Oct/2015:06:01:41 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n180.180.115.228 - - [30/Oct/2015:06:33:54 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.1\" 200 24323 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n180.180.115.228 - - [30/Oct/2015:06:33:56 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n180.180.115.228 - - [30/Oct/2015:06:33:58 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n180.180.115.228 - - [30/Oct/2015:06:33:59 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n180.180.115.228 - - [30/Oct/2015:06:34:01 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n180.180.115.228 - - [30/Oct/2015:06:34:02 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n180.180.98.36 - - [30/Oct/2015:06:41:35 +0100] \"GET /open-source HTTP/1.1\" 200 17529 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n180.180.98.36 - - [30/Oct/2015:06:41:37 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n180.180.98.36 - - [30/Oct/2015:06:41:39 +0100] \"POST /join_form HTTP/1.1\" 302 10322 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n180.180.98.36 - - [30/Oct/2015:06:41:40 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n180.180.98.36 - - [30/Oct/2015:06:41:42 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n180.180.98.36 - - [30/Oct/2015:06:41:43 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n5.157.42.183 - - [30/Oct/2015:06:46:37 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.0\" 200 24323 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n5.157.42.183 - - [30/Oct/2015:06:46:38 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n5.157.42.183 - - [30/Oct/2015:06:46:39 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n5.157.42.183 - - [30/Oct/2015:06:46:40 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n5.157.42.183 - - [30/Oct/2015:06:46:40 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n5.157.42.183 - - [30/Oct/2015:06:46:41 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.255.101.129 - - [30/Oct/2015:06:56:23 +0100] \"GET / HTTP/1.0\" 200 20478 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.255.101.129 - - [30/Oct/2015:06:56:24 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.255.101.129 - - [30/Oct/2015:06:56:25 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n69.163.47.119 - - [30/Oct/2015:06:58:17 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n117.169.1.109 - - [30/Oct/2015:06:58:54 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n117.169.1.109 - - [30/Oct/2015:06:58:57 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n117.169.1.109 - - [30/Oct/2015:06:58:59 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n117.169.1.109 - - [30/Oct/2015:06:59:01 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n117.169.1.109 - - [30/Oct/2015:06:59:04 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n155.94.215.54 - - [30/Oct/2015:07:08:44 +0100] \"GET / HTTP/1.0\" 200 20478 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n155.94.215.54 - - [30/Oct/2015:07:08:45 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n155.94.215.54 - - [30/Oct/2015:07:08:47 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n155.94.215.54 - - [30/Oct/2015:07:08:48 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n155.94.215.54 - - [30/Oct/2015:07:08:49 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n155.94.215.54 - - [30/Oct/2015:07:08:50 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n208.115.125.58 - - [30/Oct/2015:07:08:58 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n208.115.125.58 - - [30/Oct/2015:07:09:09 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n208.115.125.58 - - [30/Oct/2015:07:09:12 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n172.245.229.166 - - [30/Oct/2015:07:18:33 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36444 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n172.245.229.166 - - [30/Oct/2015:07:18:34 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n172.245.229.166 - - [30/Oct/2015:07:18:35 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n172.245.229.166 - - [30/Oct/2015:07:18:36 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n82.211.57.224 - - [30/Oct/2015:07:40:40 +0100] \"GET / HTTP/1.0\" 302 285 \"http://www.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n82.211.57.224 - - [30/Oct/2015:07:40:40 +0100] \"GET / HTTP/1.0\" 200 20478 \"http://www.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n82.211.57.224 - - [30/Oct/2015:07:40:41 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n82.211.57.224 - - [30/Oct/2015:07:40:41 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n82.211.57.224 - - [30/Oct/2015:07:40:42 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n82.211.57.224 - - [30/Oct/2015:07:40:43 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n82.211.57.224 - - [30/Oct/2015:07:40:43 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/36.1.1.21 Chrome/36.0.1985.97 Safari/537.36\"\n192.99.244.139 - - [30/Oct/2015:07:40:56 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36444 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n192.99.244.139 - - [30/Oct/2015:07:40:58 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n192.99.244.139 - - [30/Oct/2015:07:40:58 +0100] \"POST /join_form HTTP/1.1\" 302 9245 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n192.99.244.139 - - [30/Oct/2015:07:40:59 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n192.99.244.139 - - [30/Oct/2015:07:41:00 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n192.99.244.139 - - [30/Oct/2015:07:41:01 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n27.156.72.98 - - [30/Oct/2015:07:50:46 +0100] \"GET /linux/installing-my-new-server/networking HTTP/1.1\" 200 27452 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n27.156.72.98 - - [30/Oct/2015:07:50:48 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n27.156.72.98 - - [30/Oct/2015:07:50:49 +0100] \"POST /join_form HTTP/1.1\" 302 9245 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n27.156.72.98 - - [30/Oct/2015:07:50:50 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n27.156.72.98 - - [30/Oct/2015:07:50:51 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n27.156.72.98 - - [30/Oct/2015:07:50:52 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n208.115.125.58 - - [30/Oct/2015:08:01:14 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n208.115.125.58 - - [30/Oct/2015:08:01:17 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n208.115.125.58 - - [30/Oct/2015:08:01:20 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n104.168.92.23 - - [30/Oct/2015:08:21:09 +0100] \"GET /linux/installing-gitlab-on-centos-6 HTTP/1.0\" 200 19422 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n104.168.92.23 - - [30/Oct/2015:08:21:22 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n104.168.92.23 - - [30/Oct/2015:08:21:24 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n104.168.92.23 - - [30/Oct/2015:08:21:26 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n104.168.92.23 - - [30/Oct/2015:08:21:31 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n104.168.92.23 - - [30/Oct/2015:08:21:32 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n23.95.195.29 - - [30/Oct/2015:08:28:27 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n23.95.195.29 - - [30/Oct/2015:08:28:28 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n23.95.195.29 - - [30/Oct/2015:08:28:29 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n23.95.195.29 - - [30/Oct/2015:08:28:30 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n23.95.195.29 - - [30/Oct/2015:08:28:32 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n110.80.69.214 - - [30/Oct/2015:09:00:07 +0100] \"GET http://howto.basjes.nl/linux/doing-pxe-without-dhcp-control HTTP/1.0\" 200 24323 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n110.80.69.214 - - [30/Oct/2015:09:00:14 +0100] \"GET http://howto.basjes.nl/join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n110.80.69.214 - - [30/Oct/2015:09:00:29 +0100] \"POST http://howto.basjes.nl/join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n110.80.69.214 - - [30/Oct/2015:09:00:32 +0100] \"GET http://howto.basjes.nl/acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n110.80.69.214 - - [30/Oct/2015:09:00:36 +0100] \"GET http://howto.basjes.nl/login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n110.80.69.214 - - [30/Oct/2015:09:00:43 +0100] \"POST http://howto.basjes.nl/login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n158.222.4.184 - - [30/Oct/2015:09:01:43 +0100] \"GET /join_form HTTP/1.0\" 200 12191 \"http://niels.basjes.nl\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n45.62.47.10 - - [30/Oct/2015:09:01:44 +0100] \"POST /join_form HTTP/1.0\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n89.44.18.45 - - [30/Oct/2015:09:07:45 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.0\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:34.0) Gecko/20100101 Firefox/34.0\"\n89.44.18.45 - - [30/Oct/2015:09:07:47 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:34.0) Gecko/20100101 Firefox/34.0\"\n89.44.18.45 - - [30/Oct/2015:09:07:48 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:34.0) Gecko/20100101 Firefox/34.0\"\n67.17.34.8 - - [30/Oct/2015:09:09:56 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//facebookscandals.jimdo.com HTTP/1.0\" 200 11739 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n67.17.34.8 - - [30/Oct/2015:09:09:58 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/acl_users/credentials_cookie_auth/require_login?came_from=http%3A//facebookscandals.jimdo.com\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n67.17.34.8 - - [30/Oct/2015:09:10:00 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n67.17.34.8 - - [30/Oct/2015:09:10:01 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n67.17.34.8 - - [30/Oct/2015:09:10:03 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n188.240.133.156 - - [30/Oct/2015:09:12:34 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.0\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:34.0) Gecko/20100101 Firefox/34.0\"\n188.240.133.156 - - [30/Oct/2015:09:12:35 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:34.0) Gecko/20100101 Firefox/34.0\"\n188.240.133.156 - - [30/Oct/2015:09:12:36 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:34.0) Gecko/20100101 Firefox/34.0\"\n180.180.115.117 - - [30/Oct/2015:09:24:54 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.1\" 200 24323 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n180.180.115.117 - - [30/Oct/2015:09:24:56 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n180.180.115.117 - - [30/Oct/2015:09:24:58 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n180.180.115.117 - - [30/Oct/2015:09:24:59 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n180.180.115.117 - - [30/Oct/2015:09:25:01 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n180.180.115.117 - - [30/Oct/2015:09:25:15 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n104.140.71.83 - - [30/Oct/2015:09:35:41 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n104.140.71.83 - - [30/Oct/2015:09:35:42 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n104.140.71.83 - - [30/Oct/2015:09:35:43 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n192.126.192.175 - - [30/Oct/2015:09:39:48 +0100] \"GET /open-source HTTP/1.0\" 200 17529 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n192.126.192.175 - - [30/Oct/2015:09:39:50 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n192.126.192.175 - - [30/Oct/2015:09:39:52 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n192.126.192.175 - - [30/Oct/2015:09:39:54 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n192.3.242.26 - - [30/Oct/2015:09:51:33 +0100] \"GET / HTTP/1.0\" 200 15381 \"http://daniel_en_sander.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n192.3.242.26 - - [30/Oct/2015:09:51:35 +0100] \"GET /join_form HTTP/1.1\" 200 11652 \"http://daniel_en_sander.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n192.3.242.26 - - [30/Oct/2015:09:51:36 +0100] \"POST /join_form HTTP/1.1\" 302 9556 \"http://daniel_en_sander.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n192.3.242.26 - - [30/Oct/2015:09:51:37 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//daniel_en_sander.basjes.nl/join_form HTTP/1.1\" 403 340 \"http://daniel_en_sander.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n23.94.10.8 - - [30/Oct/2015:10:05:20 +0100] \"GET /linux/installing-my-new-server/networking HTTP/1.0\" 200 27452 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n23.94.10.8 - - [30/Oct/2015:10:05:35 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n23.94.10.8 - - [30/Oct/2015:10:05:52 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n23.94.10.8 - - [30/Oct/2015:10:06:09 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n23.94.10.8 - - [30/Oct/2015:10:06:12 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n23.94.10.8 - - [30/Oct/2015:10:06:27 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n212.129.17.73 - - [30/Oct/2015:10:10:17 +0100] \"GET /join_form HTTP/1.1\" 200 11652 \"http://daniel_en_sander.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n201.22.59.126 - - [30/Oct/2015:10:10:34 +0100] \"GET /join_form HTTP/1.1\" 200 11652 \"http://daniel_en_sander.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n60.191.163.235 - - [30/Oct/2015:10:11:18 +0100] \"POST /join_form HTTP/1.0\" 302 9556 \"http://daniel_en_sander.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n60.191.163.235 - - [30/Oct/2015:10:11:21 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//daniel_en_sander.basjes.nl/join_form HTTP/1.0\" 403 340 \"http://daniel_en_sander.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n89.33.71.249 - - [30/Oct/2015:10:30:03 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n89.33.71.249 - - [30/Oct/2015:10:30:04 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n89.33.71.249 - - [30/Oct/2015:10:30:05 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n89.33.71.249 - - [30/Oct/2015:10:30:05 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n89.33.71.249 - - [30/Oct/2015:10:30:06 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n23.232.137.128 - - [30/Oct/2015:10:33:53 +0100] \"GET /linux/installing-gitlab-on-centos-6 HTTP/1.0\" 200 19422 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n23.232.137.128 - - [30/Oct/2015:10:33:54 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n23.232.137.128 - - [30/Oct/2015:10:33:55 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n23.232.137.128 - - [30/Oct/2015:10:33:56 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n23.232.137.128 - - [30/Oct/2015:10:33:58 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n23.232.137.128 - - [30/Oct/2015:10:33:59 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n192.99.244.139 - - [30/Oct/2015:10:35:20 +0100] \"GET /accessibility-info HTTP/1.1\" 200 20626 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n192.99.244.139 - - [30/Oct/2015:10:35:21 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n192.99.244.139 - - [30/Oct/2015:10:35:22 +0100] \"POST /join_form HTTP/1.1\" 302 10322 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n192.99.244.139 - - [30/Oct/2015:10:35:22 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n192.99.244.139 - - [30/Oct/2015:10:35:23 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n192.99.244.139 - - [30/Oct/2015:10:35:24 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n148.251.65.34 - - [30/Oct/2015:10:45:57 +0100] \"GET /linux/installing-fedora-linux-via-pxe-x86-64 HTTP/1.1\" 200 36444 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n66.248.210.252 - - [30/Oct/2015:10:46:00 +0100] \"GET /join_form HTTP/1.0\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n66.248.210.252 - - [30/Oct/2015:10:46:01 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n66.248.210.252 - - [30/Oct/2015:10:46:02 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n66.248.210.252 - - [30/Oct/2015:10:46:03 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n66.248.210.252 - - [30/Oct/2015:10:46:04 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n180.180.118.146 - - [30/Oct/2015:10:56:19 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.1\" 200 24323 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n180.180.118.146 - - [30/Oct/2015:10:56:21 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n180.180.118.146 - - [30/Oct/2015:10:56:24 +0100] \"POST /join_form HTTP/1.1\" 302 9245 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n180.180.118.146 - - [30/Oct/2015:10:56:26 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n180.180.118.146 - - [30/Oct/2015:10:56:27 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n180.180.118.146 - - [30/Oct/2015:10:56:29 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n5.102.254.132 - - [30/Oct/2015:11:00:47 +0100] \"GET /places HTTP/1.1\" 200 19359 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n5.102.254.132 - - [30/Oct/2015:11:00:48 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n5.102.254.132 - - [30/Oct/2015:11:00:49 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n5.102.254.132 - - [30/Oct/2015:11:00:49 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n5.102.254.132 - - [30/Oct/2015:11:00:50 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n5.102.254.132 - - [30/Oct/2015:11:00:51 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n212.83.141.47 - - [30/Oct/2015:11:11:41 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n216.158.200.60 - - [30/Oct/2015:11:41:42 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.0\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n216.158.200.60 - - [30/Oct/2015:11:41:44 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n216.158.200.60 - - [30/Oct/2015:11:41:45 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n216.158.201.102 - - [30/Oct/2015:11:48:22 +0100] \"GET /open-source HTTP/1.0\" 200 17529 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n216.158.201.102 - - [30/Oct/2015:11:48:30 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n216.158.201.102 - - [30/Oct/2015:11:48:32 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n216.158.201.102 - - [30/Oct/2015:11:48:33 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n216.158.222.163 - - [30/Oct/2015:11:49:04 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.0\" 200 24323 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n216.158.222.163 - - [30/Oct/2015:11:49:05 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n216.158.222.163 - - [30/Oct/2015:11:49:07 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n216.158.222.163 - - [30/Oct/2015:11:49:08 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n158.222.8.217 - - [30/Oct/2015:11:53:02 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.0\" 200 11793 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n158.222.12.76 - - [30/Oct/2015:11:53:07 +0100] \"GET /login_form HTTP/1.0\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n216.158.197.69 - - [30/Oct/2015:11:54:15 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.0\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n216.158.197.69 - - [30/Oct/2015:11:54:16 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n216.158.197.69 - - [30/Oct/2015:11:54:18 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n104.128.23.188 - - [30/Oct/2015:11:59:40 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.0\" 200 10716 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n104.128.23.188 - - [30/Oct/2015:11:59:45 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n104.128.23.188 - - [30/Oct/2015:11:59:46 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n104.128.23.188 - - [30/Oct/2015:11:59:47 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n104.128.23.188 - - [30/Oct/2015:11:59:49 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n104.128.23.188 - - [30/Oct/2015:11:59:51 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n212.129.17.73 - - [30/Oct/2015:12:29:20 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//daniel_en_sander.basjes.nl/join_form HTTP/1.1\" 403 340 \"http://daniel_en_sander.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n212.129.17.73 - - [30/Oct/2015:12:29:51 +0100] \"GET / HTTP/1.1\" 200 15381 \"http://daniel_en_sander.basjes.nl\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n183.203.23.135 - - [30/Oct/2015:12:29:54 +0100] \"GET /join_form HTTP/1.1\" 200 11652 \"http://daniel_en_sander.basjes.nl\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n183.203.23.135 - - [30/Oct/2015:12:29:57 +0100] \"POST /join_form HTTP/1.1\" 302 9556 \"http://daniel_en_sander.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n183.203.23.135 - - [30/Oct/2015:12:30:00 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//daniel_en_sander.basjes.nl/join_form HTTP/1.1\" 403 340 \"http://daniel_en_sander.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n88.150.210.88 - - [30/Oct/2015:12:33:31 +0100] \"GET /join_form HTTP/1.0\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n88.150.210.88 - - [30/Oct/2015:12:33:32 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n104.144.18.94 - - [30/Oct/2015:12:44:59 +0100] \"GET / HTTP/1.0\" 200 20478 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n104.144.18.94 - - [30/Oct/2015:12:45:00 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n104.144.18.94 - - [30/Oct/2015:12:45:02 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n104.144.18.94 - - [30/Oct/2015:12:45:03 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n104.144.18.94 - - [30/Oct/2015:12:45:05 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n104.144.18.94 - - [30/Oct/2015:12:45:08 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n199.195.159.11 - - [30/Oct/2015:12:49:24 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n199.195.159.11 - - [30/Oct/2015:12:49:25 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n199.195.159.11 - - [30/Oct/2015:12:49:25 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n199.195.159.11 - - [30/Oct/2015:12:49:26 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n199.195.159.11 - - [30/Oct/2015:12:49:27 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n180.180.115.137 - - [30/Oct/2015:12:56:38 +0100] \"GET /linux/doing-pxe-without-dhcp-control HTTP/1.1\" 200 24323 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n180.180.115.137 - - [30/Oct/2015:12:56:46 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n180.180.115.137 - - [30/Oct/2015:12:56:47 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n180.180.115.137 - - [30/Oct/2015:12:56:49 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n180.180.115.137 - - [30/Oct/2015:12:56:57 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n180.180.115.137 - - [30/Oct/2015:12:57:02 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n23.254.164.173 - - [30/Oct/2015:14:35:34 +0100] \"GET /linux/installing-my-new-server/voice-over-ip/RK=0/RS=YTkYTnGaf5QQxT7cm9uJgKS2rl8- HTTP/1.1\" 404 12146 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.63 Safari/537.36\"\n23.254.164.173 - - [30/Oct/2015:14:35:35 +0100] \"GET /join_form HTTP/1.1\" 200 11114 \"http://howto.basjes.nl/linux/installing-my-new-server/voice-over-ip/RK=0/RS=YTkYTnGaf5QQxT7cm9uJgKS2rl8-\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.63 Safari/537.36\"\n23.254.164.173 - - [30/Oct/2015:14:35:36 +0100] \"POST /join_form HTTP/1.1\" 302 9093 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.63 Safari/537.36\"\n23.254.164.173 - - [30/Oct/2015:14:35:37 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//howto.basjes.nl/join_form HTTP/1.1\" 200 10716 \"http://howto.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.63 Safari/537.36\"\n23.254.164.173 - - [30/Oct/2015:14:35:38 +0100] \"GET /login_form HTTP/1.1\" 200 10543 \"http://howto.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.63 Safari/537.36\"\n23.254.164.173 - - [30/Oct/2015:14:35:40 +0100] \"POST /login_form HTTP/1.1\" 200 16810 \"http://howto.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.63 Safari/537.36\"\n89.32.251.246 - - [30/Oct/2015:14:41:24 +0100] \"GET /join_form HTTP/1.0\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n89.32.251.246 - - [30/Oct/2015:14:41:25 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n89.32.251.246 - - [30/Oct/2015:14:41:26 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n89.32.251.246 - - [30/Oct/2015:14:41:27 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n89.32.251.246 - - [30/Oct/2015:14:41:28 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n117.240.187.35 - - [30/Oct/2015:14:57:32 +0100] \"GET /places HTTP/1.0\" 200 19359 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n117.240.187.35 - - [30/Oct/2015:14:57:39 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n117.240.187.35 - - [30/Oct/2015:14:57:43 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n117.240.187.35 - - [30/Oct/2015:14:57:47 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n117.240.187.35 - - [30/Oct/2015:14:57:51 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n117.240.187.35 - - [30/Oct/2015:14:57:54 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n192.161.57.88 - - [30/Oct/2015:15:59:34 +0100] \"GET /join_form HTTP/1.1\" 200 12191 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n192.161.57.88 - - [30/Oct/2015:15:59:35 +0100] \"POST /join_form HTTP/1.1\" 302 10170 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n192.161.57.88 - - [30/Oct/2015:15:59:36 +0100] \"GET /acl_users/credentials_cookie_auth/require_login?came_from=http%3A//niels.basjes.nl/join_form HTTP/1.1\" 200 11793 \"http://niels.basjes.nl/join_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n192.161.57.88 - - [30/Oct/2015:15:59:38 +0100] \"GET /login_form HTTP/1.1\" 200 11620 \"http://niels.basjes.nl/\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n192.161.57.88 - - [30/Oct/2015:15:59:39 +0100] \"POST /login_form HTTP/1.1\" 200 18354 \"http://niels.basjes.nl/login_form\" \"Mozilla/5.0 (Windows NT 6.1; rv:32.0) Gecko/20100101 Firefox/32.0\"\n"
  },
  {
    "path": "examples/java-pojo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n Apache HTTPD & NGINX Access log parsing made easy\n Copyright (C) 2011-2023 Niels Basjes\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n https://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n-->\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n  <modelVersion>4.0.0</modelVersion>\n  <parent>\n    <artifactId>httpdlog-examples</artifactId>\n    <groupId>nl.basjes.parse.httpdlog.examples</groupId>\n    <version>6.0.1-SNAPSHOT</version>\n  </parent>\n  <artifactId>java-pojo</artifactId>\n  <name>Parser - Examples - Java POJO</name>\n\n  <dependencies>\n    <dependency>\n      <groupId>nl.basjes.parse.httpdlog</groupId>\n      <artifactId>httpdlog-parser</artifactId>\n      <version>${project.version}</version>\n    </dependency>\n\n    <dependency>\n      <groupId>org.slf4j</groupId>\n      <artifactId>slf4j-reload4j</artifactId>\n      <version>${slf4j.version}</version>\n    </dependency>\n\n  </dependencies>\n\n  <build>\n    <plugins>\n      <plugin>\n        <groupId>org.apache.maven.plugins</groupId>\n        <artifactId>maven-deploy-plugin</artifactId>\n        <configuration>\n          <skip>true</skip>\n        </configuration>\n      </plugin>\n    </plugins>\n  </build>\n</project>\n"
  },
  {
    "path": "examples/java-pojo/src/main/java/nl/basjes/parse/Main.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse;\n\nimport nl.basjes.parse.core.Parser;\nimport nl.basjes.parse.core.exceptions.DissectionFailure;\nimport nl.basjes.parse.core.exceptions.InvalidDissectorException;\nimport nl.basjes.parse.core.exceptions.MissingDissectorsException;\nimport nl.basjes.parse.httpdlog.HttpdLoglineParser;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.util.List;\n\npublic final class Main {\n    private Main(){}\n\n    private static final Logger LOG = LoggerFactory.getLogger(Main.class);\n\n    private void printAllPossibles(String logformat) throws NoSuchMethodException, MissingDissectorsException, InvalidDissectorException {\n        // To figure out what values we CAN get from this line we instantiate the parser with a dummy class\n        // that does not have ANY @Field annotations.\n        Parser<Object> dummyParser= new HttpdLoglineParser<>(Object.class, logformat);\n\n        List<String> possiblePaths;\n        possiblePaths = dummyParser.getPossiblePaths();\n\n        // If you want to call 'getCasts' then the actual parser needs to be constructed.\n        // Simply calling getPossiblePaths does not build the actual parser.\n        // Because we want this for all possibilities yet we are never actually going to use this instance of the parser\n        // We simply give it a random method with the right signature and tell it we want all possible paths\n        dummyParser.addParseTarget(String.class.getMethod(\"indexOf\", String.class), possiblePaths);\n\n        LOG.info(\"==================================\");\n        LOG.info(\"Possible output:\");\n        for (String path : possiblePaths) {\n            LOG.info(\"{}     {}\", path, dummyParser.getCasts(path));\n        }\n        LOG.info(\"==================================\");\n    }\n\n    private void run() throws InvalidDissectorException, MissingDissectorsException, NoSuchMethodException, DissectionFailure {\n\n        // This format and logline originate from here:\n        // https://stackoverflow.com/questions/20349184/java-parse-log-file\n        String logformat = \"%t %u [%D %h %{True-Client-IP}i %{UNIQUE_ID}e %r] %{Cookie}i %s \\\"%{User-Agent}i\\\" \\\"%{host}i\\\" %l %b %{Referer}i\";\n        String logline = \"[02/Dec/2013:14:10:30 -0000] - [52075 10.102.4.254 177.43.52.210 UpyU1gpmBAwAACfd5W0AAAAW GET /SS14-VTam-ny_019.j\" +\n                \"pg.rendition.zoomable.jpg HTTP/1.1] hsfirstvisit=http%3A%2F%2Fwww.domain.com%2Fen-us||1372268254000; _opt_vi_3FNG8DZU=F870\" +\n                \"DCFD-CBA4-4B6E-BB58-4605A78EE71A; __ptca=145721067.0aDxsZlIuM48.1372279055.1379945057.1379950362.9; __ptv_62vY4e=0aDxsZlIu\" +\n                \"M48; __pti_62vY4e=0aDxsZlIuM48; __ptcz=145721067.1372279055.1.0.ptmcsr=(direct)|ptmcmd=(none)|ptmccn=(direct); __hstc=1457\" +\n                \"21067.b86362bb7a1d257bfa2d1fb77e128a85.1372268254968.1379934256743.1379939561848.9; hubspotutk=b86362bb7a1d257bfa2d1fb77e1\" +\n                \"28a85; USER_GROUP=julinho%3Afalse; has_js=1; WT_FPC=id=177.43.52.210-1491335248.30301337:lv=1385997780893:ss=1385997780893\" +\n                \"; dtCookie=1F2E0E1037589799D8D503EB8CFA12A1|_default|1; RM=julinho%3A5248423ad3fe062f06c54915e6cde5cb45147977; wcid=UpyKsQ\" +\n                \"pmBAwAABURyNoAAAAS%3A35d8227ba1e8a9a9cebaaf8d019a74777c32b4c8; Carte::KerberosLexicon_getWGSN=82ae3dcd1b956288c3c86bdbed6e\" +\n                \"bcc0fd040e1e; UserData=Username%3AJULINHO%3AHomepage%3A1%3AReReg%3A0%3ATrialist%3A0%3ALanguage%3Aen%3ACcode%3Abr%3AForceRe\" +\n                \"Reg%3A0; UserID=1356673%3A12345%3A1234567890%3A123%3Accode%3Abr; USER_DATA=1356673%3Ajulinho%3AJulio+Jose%3Ada+Silva%3Ajul\" +\n                \"inho%40tecnoblu.com.br%3A0%3A1%3Aen%3Abr%3A%3AWGSN%3A1385990833.81925%3A82ae3dcd1b956288c3c86bdbed6ebcc0fd040e1e; MODE=FON\" +\n                \"TIS; SECTION=%2Fcontent%2Fsection%2Fhome.html; edge_auth=ip%3D177.43.52.210~expires%3D1385994522~access%3D%2Fapps%2F%2A%21\" +\n                \"%2Fbin%2F%2A%21%2Fcontent%2F%2A%21%2Fetc%2F%2A%21%2Fhome%2F%2A%21%2Flibs%2F%2A%21%2Freport%2F%2A%21%2Fsection%2F%2A%21%2Fw\" +\n                \"gsn%2F%2A~md5%3D90e73ee10161c1afacab12c6ea30b4ef; __utma=94539802.1793276213.1372268248.1385572390.1385990581.16; __utmb=9\" +\n                \"4539802.52.9.1385991739764; __utmc=94539802; __utmz=94539802.1372268248.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none);\" +\n                \" WT_FPC=id=177.43.52.210-1491335248.30301337:lv=1386000374581:ss=1386000374581; dtPC=-; NSC_wtfswfs_xfcgbsn40-41=ffffffff0\" +\n                \"96e1a1d45525d5f4f58455e445a4a423660; akamai-edge=5ac6e5b3d0bbe2ea771bb2916d8bab34ea222a6a 200 \\\"Mozilla/5.0 (Windows NT 6.\" +\n                \"2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.57 Safari/537.36\\\" \\\"www.domain.com\\\" - 463952 http://ww\" +\n                \"w.domain.com/content/report/shows/New_York/KSHK/trip/s_s_14_ny_ww/sheers.html\";\n\n        printAllPossibles(logformat);\n\n        Parser<MyRecord> parser = new HttpdLoglineParser<>(MyRecord.class, logformat);\n        MyRecord record = new MyRecord();\n\n        LOG.info(\"==================================================================================\");\n        parser.parse(record, logline);\n        LOG.info(record.toString());\n        LOG.info(\"==================================================================================\");\n    }\n\n    public static void main(final String[] args) throws Exception {\n        new Main().run();\n    }\n\n}\n"
  },
  {
    "path": "examples/java-pojo/src/main/java/nl/basjes/parse/MyRecord.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse;\n\nimport nl.basjes.parse.core.Field;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.TreeSet;\n\npublic class MyRecord {\n\n    private final Map<String, String> results = new HashMap<>(32);\n\n    @Field(\"STRING:request.firstline.uri.query.*\")\n    public void setQueryDeepMany(final String name, final String value) {\n        results.put(name, value);\n    }\n\n    @Field(\"STRING:request.firstline.uri.query.img\")\n    public void setQueryImg(final String name, final String value) {\n        results.put(name, value);\n    }\n\n    @Field(\"IP:connection.client.host\")\n    public void setIP(final String value) {\n        results.put(\"IP:connection.client.host\", value);\n    }\n\n    @Field({\n        \"HTTP.QUERYSTRING:request.firstline.uri.query\",\n        \"NUMBER:connection.client.logname\",\n        \"STRING:connection.client.user\",\n        \"TIME.STAMP:request.receive.time\",\n        \"HTTP.URI:request.firstline.uri\",\n        \"BYTESCLF:response.body.bytes\",\n        \"HTTP.URI:request.referer\",\n        \"HTTP.USERAGENT:request.user-agent\",\n        \"TIME.DAY:request.receive.time.day\",\n        \"TIME.HOUR:request.receive.time.hour\",\n        \"TIME.MONTHNAME:request.receive.time.monthname\"\n        })\n    public void setValue(final String name, final String value) {\n        results.put(name, value);\n    }\n\n    public String toString() {\n        StringBuilder sb = new StringBuilder();\n        TreeSet<String> keys = new TreeSet<>(results.keySet());\n        for (String key : keys) {\n            sb.append(key).append(\" = \").append(results.get(key)).append('\\n');\n        }\n\n        return sb.toString();\n    }\n\n    public void clear() {\n        results.clear();\n    }\n}\n"
  },
  {
    "path": "examples/java-pojo/src/main/resources/log4j.properties",
    "content": "#\n# Apache HTTPD & NGINX Access log parsing made easy\n# Copyright (C) 2011-2023 Niels Basjes\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# Root logger option\nlog4j.rootLogger=INFO, stdout\n#, file\nlog4j.appender.stdout=org.apache.log4j.ConsoleAppender\nlog4j.appender.stdout.Target=System.out\nlog4j.appender.stdout.threshold=INFO\nlog4j.appender.stdout.layout=org.apache.log4j.PatternLayout\nlog4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} [%-5p] %-40c{1}:%5L: %m%n\n\n## file appender\n#log4j.appender.file=org.apache.log4j.RollingFileAppender\n#log4j.appender.file.File=target/debug.log\n#log4j.appender.file.threshold=DEBUG\n#log4j.appender.file.layout=org.apache.log4j.PatternLayout\n#log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd} %d{ABSOLUTE} [%-5p] %-40c{1}:%5L: %m%n\n#log4j.appender.file.Append=false\n"
  },
  {
    "path": "examples/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n Apache HTTPD & NGINX Access log parsing made easy\n Copyright (C) 2011-2023 Niels Basjes\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n https://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n-->\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n  <modelVersion>4.0.0</modelVersion>\n  <parent>\n    <artifactId>parser-parent</artifactId>\n    <groupId>nl.basjes.parse</groupId>\n    <version>6.0.1-SNAPSHOT</version>\n  </parent>\n\n  <groupId>nl.basjes.parse.httpdlog.examples</groupId>\n  <artifactId>httpdlog-examples</artifactId>\n  <packaging>pom</packaging>\n  <name>Parser - Examples -</name>\n\n  <build>\n    <plugins>\n      <plugin>\n        <groupId>org.apache.maven.plugins</groupId>\n        <artifactId>maven-deploy-plugin</artifactId>\n        <configuration>\n          <skip>true</skip>\n        </configuration>\n      </plugin>\n      <plugin>\n        <groupId>org.apache.maven.plugins</groupId>\n        <artifactId>maven-checkstyle-plugin</artifactId>\n      </plugin>\n\n      <!-- Disable coverage analysis for this module -->\n      <plugin>\n        <groupId>org.jacoco</groupId>\n        <artifactId>jacoco-maven-plugin</artifactId>\n        <configuration>\n          <skip>true</skip>\n        </configuration>\n      </plugin>\n\n      <!-- Disable dependency convergence checks on the examples -->\n      <plugin>\n        <groupId>org.apache.maven.plugins</groupId>\n        <artifactId>maven-enforcer-plugin</artifactId>\n        <executions>\n          <execution>\n            <id>dependency-convergence</id>\n            <phase>none</phase>\n          </execution>\n        </executions>\n      </plugin>\n\n    </plugins>\n  </build>\n\n  <modules>\n    <module>java-pojo</module>\n    <module>apache-hadoop-mapreduce</module>\n    <module>apache-flink</module>\n    <module>apache-beam</module>\n  </modules>\n\n</project>\n"
  },
  {
    "path": "httpdlog/httpdlog-inputformat/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n Apache HTTPD & NGINX Access log parsing made easy\n Copyright (C) 2011-2023 Niels Basjes\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n https://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n-->\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n  <modelVersion>4.0.0</modelVersion>\n  <parent>\n    <artifactId>httpdlog</artifactId>\n    <groupId>nl.basjes.parse.httpdlog</groupId>\n    <version>6.0.1-SNAPSHOT</version>\n  </parent>\n  <artifactId>httpdlog-inputformat</artifactId>\n  <name>Parser - Apache HTTPD - Hadoop InputFormat</name>\n\n  <properties>\n    <!-- The Hadoop dependencies are too hard to make this check pass -->\n    <depencency-convergence.phase>none</depencency-convergence.phase>\n  </properties>\n\n  <dependencies>\n\n    <dependency>\n      <groupId>org.apache.hadoop</groupId>\n      <artifactId>hadoop-client</artifactId>\n      <version>${hadoop.version}</version>\n      <scope>provided</scope>\n    </dependency>\n\n    <dependency>\n      <groupId>${project.groupId}</groupId>\n      <artifactId>httpdlog-parser</artifactId>\n      <version>${project.version}</version>\n    </dependency>\n\n    <dependency>\n      <groupId>nl.basjes.parse</groupId>\n      <artifactId>parser-core</artifactId>\n      <version>${project.version}</version>\n      <classifier>tests</classifier>\n      <scope>test</scope>\n    </dependency>\n\n  </dependencies>\n\n  <build>\n    <plugins>\n      <plugin>\n        <groupId>org.jacoco</groupId>\n        <artifactId>jacoco-maven-plugin</artifactId>\n      </plugin>\n    </plugins>\n  </build>\n</project>\n"
  },
  {
    "path": "httpdlog/httpdlog-inputformat/src/main/assembly/job.xml",
    "content": "<!--\n Apache HTTPD & NGINX Access log parsing made easy\n Copyright (C) 2011-2023 Niels Basjes\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n https://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n-->\n<assembly\n  xmlns=\"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2\"\n  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n  xsi:schemaLocation=\"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd\">\n  <id>job</id>\n  <formats>\n    <format>jar</format>\n  </formats>\n  <includeBaseDirectory>false</includeBaseDirectory>\n  <dependencySets>\n    <dependencySet>\n      <useProjectArtifact>false</useProjectArtifact>\n      <outputDirectory>lib</outputDirectory>\n      <unpack>false</unpack>\n      <scope>compile</scope>\n      <excludes>\n        <exclude>org.slf4j:slf4j-api</exclude>\n      </excludes>\n    </dependencySet>\n    <!--\n    <dependencySet>\n      <useProjectArtifact>false</useProjectArtifact>\n      <outputDirectory>lib</outputDirectory>\n      <unpack>false</unpack>\n      <scope>provided</scope>\n    </dependencySet>\n    -->\n  </dependencySets>\n  <fileSets>\n    <fileSet>\n      <directory>${project.build.outputDirectory}</directory>\n      <outputDirectory>${file.separator}</outputDirectory>\n    </fileSet>\n  </fileSets>\n</assembly>\n"
  },
  {
    "path": "httpdlog/httpdlog-inputformat/src/main/java/nl/basjes/hadoop/input/ApacheHttpdLogfileInputFormat.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.hadoop.input;\n\nimport nl.basjes.parse.core.Dissector;\nimport nl.basjes.parse.httpdlog.HttpdLoglineParser;\nimport org.apache.hadoop.fs.Path;\nimport org.apache.hadoop.io.LongWritable;\nimport org.apache.hadoop.io.compress.CompressionCodec;\nimport org.apache.hadoop.io.compress.CompressionCodecFactory;\nimport org.apache.hadoop.io.compress.SplittableCompressionCodec;\nimport org.apache.hadoop.mapreduce.InputSplit;\nimport org.apache.hadoop.mapreduce.JobContext;\nimport org.apache.hadoop.mapreduce.RecordReader;\nimport org.apache.hadoop.mapreduce.TaskAttemptContext;\nimport org.apache.hadoop.mapreduce.lib.input.FileInputFormat;\n\nimport java.io.IOException;\nimport java.util.Collection;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\npublic class ApacheHttpdLogfileInputFormat extends\n        FileInputFormat<LongWritable, ParsedRecord> {\n\n    private String logFormat = null;\n    private final Set<String> requestedFields = new HashSet<>();\n    private Map<String, Set<String>> typeRemappings;\n    private List<Dissector> additionalDissectors;\n    private ApacheHttpdLogfileRecordReader theRecordReader;\n    // --------------------------------------------\n\n    public List<String> listPossibleFields(String logformat) {\n        return listPossibleFields(logformat, typeRemappings, additionalDissectors);\n    }\n\n    public static List<String> listPossibleFields(String logformat, Map<String, Set<String>> typeRemappings, List<Dissector> additionalDissectors) {\n        HttpdLoglineParser<ParsedRecord> parser = new HttpdLoglineParser<>(ParsedRecord.class, logformat);\n        parser.setTypeRemappings(typeRemappings);\n        parser.addDissectors(additionalDissectors);\n        return parser.getPossiblePaths();\n    }\n\n\n    public String getLogFormat() {\n        return logFormat;\n    }\n\n    public Set<String> getRequestedFields() {\n        return requestedFields;\n    }\n\n    public Map<String, Set<String>> getTypeRemappings() {\n        return typeRemappings;\n    }\n\n    public List<Dissector> getAdditionalDissectors() {\n        return additionalDissectors;\n    }\n\n    public ApacheHttpdLogfileInputFormat() {\n        super();\n    }\n\n    public ApacheHttpdLogfileInputFormat(\n            String logformat,\n            Collection<String> requestedFields,\n            Map<String, Set<String>> typeRemappings,\n            List<Dissector> additionalDissectors) {\n        super();\n        this.logFormat = logformat;\n        this.requestedFields.addAll(requestedFields);\n        this.typeRemappings = typeRemappings;\n        this.additionalDissectors = additionalDissectors;\n    }\n\n    // --------------------------------------------\n\n    public ApacheHttpdLogfileRecordReader createRecordReader() {\n        try {\n            return new ApacheHttpdLogfileRecordReader(getLogFormat(), getRequestedFields(), getTypeRemappings(), getAdditionalDissectors());\n        } catch (IOException e) {\n            return null;\n        }\n    }\n\n    public ApacheHttpdLogfileRecordReader getRecordReader() {\n        if (theRecordReader == null) {\n            theRecordReader = createRecordReader();\n        }\n        return theRecordReader;\n    }\n\n    @Override\n    public RecordReader<LongWritable, ParsedRecord> createRecordReader(\n            final InputSplit split, final TaskAttemptContext context) {\n        return getRecordReader();\n    }\n\n    @Override\n    protected boolean isSplitable(JobContext context, Path file) {\n        final CompressionCodec codec =\n            new CompressionCodecFactory(context.getConfiguration()).getCodec(file);\n        return (null == codec) || codec instanceof SplittableCompressionCodec;\n    }\n\n    public void setTypeRemappings(Map<String, Set<String>> newTypeRemappings) {\n        this.typeRemappings = newTypeRemappings;\n    }\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-inputformat/src/main/java/nl/basjes/hadoop/input/ApacheHttpdLogfileRecordReader.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.hadoop.input;\n\nimport nl.basjes.parse.core.Casts;\nimport nl.basjes.parse.core.Dissector;\nimport nl.basjes.parse.core.Parser;\nimport nl.basjes.parse.core.exceptions.DissectionFailure;\nimport nl.basjes.parse.core.exceptions.InvalidDissectorException;\nimport nl.basjes.parse.core.exceptions.MissingDissectorsException;\nimport nl.basjes.parse.httpdlog.HttpdLoglineParser;\nimport org.apache.hadoop.conf.Configuration;\nimport org.apache.hadoop.io.LongWritable;\nimport org.apache.hadoop.mapreduce.Counter;\nimport org.apache.hadoop.mapreduce.InputSplit;\nimport org.apache.hadoop.mapreduce.RecordReader;\nimport org.apache.hadoop.mapreduce.TaskAttemptContext;\nimport org.apache.hadoop.mapreduce.lib.input.LineRecordReader;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.EnumSet;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\n@SuppressWarnings({ \"PMD.OnlyOneReturn\", \"PMD.BeanMembersShouldSerialize\" })\npublic class ApacheHttpdLogfileRecordReader extends\n        RecordReader<LongWritable, ParsedRecord> {\n\n    private static final Logger LOG = LoggerFactory.getLogger(ApacheHttpdLogfileRecordReader.class);\n\n    private static final String HTTPD_LOGFILE_INPUT_FORMAT = \"HTTPD Access Logfile InputFormat\";\n    public static final String FIELDS = \"fields\";\n\n    // --------------------------------------------\n\n    private final LineRecordReader                 lineReader      = new LineRecordReader();\n    private Parser<ParsedRecord>                   parser;\n    private List<String> fieldList = null;\n\n    private final ParsedRecord                     currentValue    = new ParsedRecord();\n\n    private String                                 logformat       = null;\n    private final Set<String>                      requestedFields = new HashSet<>();\n    private Map<String, Set<String>>               typeRemappings  = new HashMap<>(16);\n    private List<Dissector>                        additionalDissectors;\n\n    // --------------------------------------------\n\n    @SuppressWarnings(\"unused\") // Used by the Hadoop framework\n    public ApacheHttpdLogfileRecordReader() {\n        // Nothing to do here\n    }\n\n    public ApacheHttpdLogfileRecordReader(String logformat,\n            Set<String> requestedFields,\n            Map<String, Set<String>> typeRemappings,\n            List<Dissector> additionalDissectors) throws IOException {\n        setLogFormat(logformat);\n        // Mappings and additional parsers MUST come before the requested fields\n        this.typeRemappings = typeRemappings;\n        this.additionalDissectors = additionalDissectors;\n        addRequestedFields(requestedFields);\n\n    }\n\n    private void addRequestedFields(Set<String> newRequestedFields) throws IOException {\n        requestedFields.addAll(newRequestedFields);\n        fieldList = new ArrayList<>(requestedFields);\n        try {\n            setupFields();\n        } catch (NoSuchMethodException | MissingDissectorsException | InvalidDissectorException e) {\n            throw new IOException(\"RecordReader initialization failed\", e);\n        }\n    }\n\n    private void setLogFormat(String newLogformat) {\n        if (newLogformat == null) {\n            return;\n        }\n        logformat = newLogformat;\n    }\n\n    private boolean                         outputAllPossibleFields = false;\n    private String                          allPossiblePathsFieldName;\n    private List<String>                    allPossiblePaths = null;\n\n    private Counter counterLinesRead;\n    private Counter counterGoodLines;\n    private Counter counterBadLines;\n\n    @Override\n    public void initialize(final InputSplit split,\n            final TaskAttemptContext context) throws IOException {\n        lineReader.initialize(split, context);\n        final Configuration conf = context.getConfiguration();\n\n        counterLinesRead = context.getCounter(HTTPD_LOGFILE_INPUT_FORMAT, \"1:Lines read\");\n        counterGoodLines = context.getCounter(HTTPD_LOGFILE_INPUT_FORMAT, \"2:Good lines\");\n        counterBadLines  = context.getCounter(HTTPD_LOGFILE_INPUT_FORMAT, \"3:Bad lines\");\n\n        if (logformat == null || requestedFields.isEmpty()) {\n            if (logformat == null) {\n                logformat = conf.get(\"nl.basjes.parse.apachehttpdlogline.format\", \"common\");\n            }\n            if (requestedFields.isEmpty()) {\n                String fields = conf.get(\n                        \"nl.basjes.parse.apachehttpdlogline.fields\", null);\n\n                if (fields != null) {\n                    fieldList = Arrays.asList(fields.split(\",\"));\n                }\n            } else {\n                fieldList = new ArrayList<>(requestedFields);\n            }\n        }\n\n        if (fieldList != null) {\n            if (logformat != null && parser == null) {\n                parser = createParser();\n            }\n            for (String field : fieldList) {\n                currentValue.declareRequestedFieldname(field);\n            }\n        }\n\n        try {\n            setupFields();\n        } catch (NoSuchMethodException | MissingDissectorsException | InvalidDissectorException e) {\n            throw new IOException(\"RecordReader initialization failed\", e);\n        }\n    }\n\n    protected Parser<ParsedRecord> instantiateParser(String logFormat)  {\n        return new HttpdLoglineParser<>(ParsedRecord.class, logFormat)\n            .setTypeRemappings(typeRemappings)\n            .addDissectors(additionalDissectors);\n    }\n\n    private Map<String, EnumSet<Casts>> allCasts;\n    private void setupFields() throws MissingDissectorsException, InvalidDissectorException, NoSuchMethodException, IOException {\n        if (fieldList == null || fieldList.isEmpty()) {\n            return; // Nothing to do here\n        }\n        String firstField = fieldList.get(0);\n        if (fieldList.size() == 1 &&\n            firstField.toLowerCase().trim().equals(FIELDS)) {\n            outputAllPossibleFields = true;\n            allPossiblePaths = getParser().getPossiblePaths();\n            allPossiblePathsFieldName = firstField;\n            Parser<ParsedRecord> newParser = instantiateParser(logformat)\n                .addParseTarget(ParsedRecord.class.getMethod(\"set\", String.class, String.class), allPossiblePaths)\n                .addTypeRemappings(typeRemappings);\n            allCasts = newParser.getAllCasts();\n        }\n    }\n\n    public EnumSet<Casts> getCasts(String name) throws IOException {\n        if (outputAllPossibleFields) {\n            return allCasts.get(name);\n        }\n        try {\n            return getParser().getCasts(name);\n        } catch (MissingDissectorsException | InvalidDissectorException e) {\n            throw new IOException(\"Fatal error in the parser\", e);\n        }\n    }\n\n    public Parser<ParsedRecord> getParser() throws IOException {\n        if (parser == null) {\n            parser = createParser();\n        }\n        return parser;\n    }\n\n    private Parser<ParsedRecord> createParser() throws IOException {\n        if (fieldList == null || logformat == null) {\n            return null;\n        }\n\n        Parser<ParsedRecord> newParser;\n        try {\n            newParser = instantiateParser(logformat);\n\n            for (String field: fieldList) {\n                if (field.endsWith(\".*\")) {\n                    newParser.addParseTarget(ParsedRecord.class.getMethod(\"setMultiValueString\",\n                            String.class, String.class), field);\n                } else {\n                    newParser.addParseTarget(ParsedRecord.class.getMethod(\"set\",\n                            String.class, String.class), field);\n                    newParser.addParseTarget(ParsedRecord.class.getMethod(\"set\",\n                            String.class, Long.class), field);\n                    newParser.addParseTarget(ParsedRecord.class.getMethod(\"set\",\n                            String.class, Double.class), field);\n                }\n            }\n\n        } catch (NoSuchMethodException\n                |SecurityException e) {\n            throw new IOException(e.toString());\n        }\n        return newParser;\n    }\n\n    // --------------------------------------------\n\n    private int errorLinesLogged = 0;\n    private static final int MAX_ERROR_LINES_LOGGED = 10;\n\n    @Override\n    public boolean nextKeyValue() throws IOException {\n        if (outputAllPossibleFields) {\n            // We now ONLY return the possible names of the fields that can be requested\n            if (allPossiblePaths.isEmpty()) {\n                return false;\n            }\n\n            currentValue.clear();\n\n            String value = allPossiblePaths.get(0);\n            allPossiblePaths.remove(0);\n            currentValue.set(allPossiblePathsFieldName, value);\n            return true;\n        } else {\n            boolean haveValue = false;\n            while (!haveValue) {\n                if (!lineReader.nextKeyValue()) {\n                    return false;\n                }\n\n                counterLinesRead.increment(1L);\n\n                currentValue.clear();\n                String inputLine = lineReader.getCurrentValue().toString();\n                try {\n                    getParser().parse(currentValue, lineReader.getCurrentValue().toString());\n                    counterGoodLines.increment(1L);\n                    haveValue = true;\n                } catch (DissectionFailure e) {\n                    counterBadLines.increment(1L);\n                    if (errorLinesLogged < MAX_ERROR_LINES_LOGGED) {\n                        LOG.error(\"Parse error >>>{}<<< in line: >>>{}<<<\", e.getMessage(), inputLine);\n                        errorLinesLogged++;\n                        if (errorLinesLogged == MAX_ERROR_LINES_LOGGED) {\n                            LOG.error(\">>>>>>>>>>> We now stop logging parse errors! <<<<<<<<<<<\");\n                        }\n                    }\n                    // Ignore bad lines and simply continue\n                } catch (InvalidDissectorException e) {\n                    LOG.error(\"InvalidDissectorException >>>{}<<<\", e.getMessage());\n                    return false;\n                } catch (MissingDissectorsException e) {\n                    LOG.error(\"MissingDissectorsException >>>{}<<<\", e.getMessage());\n                    return false;\n                }\n            }\n        }\n        return true;\n    }\n\n    @Override\n    public LongWritable getCurrentKey() {\n        // The key we return is the same byte offset as the TextInputFormat\n        // would give.\n        return lineReader.getCurrentKey();\n    }\n\n    @Override\n    public ParsedRecord getCurrentValue() {\n        return currentValue;\n    }\n\n    @Override\n    public float getProgress() throws IOException {\n        return lineReader.getProgress();\n    }\n\n    // --------------------------------------------\n\n    @Override\n    public void close() throws IOException {\n        lineReader.close();\n    }\n\n    // --------------------------------------------\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-inputformat/src/main/java/nl/basjes/hadoop/input/ParsedRecord.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.hadoop.input;\n\nimport org.apache.hadoop.io.Writable;\n\nimport java.io.DataInput;\nimport java.io.DataOutput;\nimport java.io.IOException;\nimport java.util.HashMap;\nimport java.util.Map;\n\npublic class ParsedRecord implements Writable {\n\n    private final Map<String, String> stringValues = new HashMap<>();\n    private final Map<String, Long> longValues = new HashMap<>();\n    private final Map<String, Double> doubleValues = new HashMap<>();\n    private final Map<String, Map<String, String>> stringSetValues = new HashMap<>();\n    private final Map<String, String> stringSetPrefixes = new HashMap<>();\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o) {\n            return true;\n        }\n\n        if (!(o instanceof ParsedRecord)) {\n            return false;\n        }\n\n        ParsedRecord that = (ParsedRecord) o;\n\n        return\n            stringValues.equals(that.stringValues)              &&\n            longValues.equals(that.longValues)                  &&\n            doubleValues.equals(that.doubleValues)              &&\n            stringSetPrefixes.equals(that.stringSetPrefixes)    &&\n            stringSetValues.equals(that.stringSetValues);\n    }\n\n    @Override\n    public int hashCode() {\n        int result = stringValues.hashCode();\n        result = 31 * result + longValues.hashCode();\n        result = 31 * result + doubleValues.hashCode();\n        result = 31 * result + stringSetValues.hashCode();\n        return result;\n    }\n\n    @Override\n    public void write(DataOutput out) throws IOException {\n        out.writeInt(stringValues.size());\n        for (Map.Entry<String, String> e : stringValues.entrySet()) {\n            out.writeUTF(e.getKey());\n            out.writeUTF(e.getValue());\n        }\n\n        out.writeInt(longValues.size());\n        for (Map.Entry<String, Long> e : longValues.entrySet()) {\n            out.writeUTF(e.getKey());\n            out.writeLong(e.getValue());\n        }\n\n        out.writeInt(doubleValues.size());\n        for (Map.Entry<String, Double> e : doubleValues.entrySet()) {\n            out.writeUTF(e.getKey());\n            out.writeDouble(e.getValue());\n        }\n\n        out.writeInt(stringSetPrefixes.size());\n        for (Map.Entry<String, String> e : stringSetPrefixes.entrySet()) {\n            out.writeUTF(e.getKey());\n            out.writeUTF(e.getValue());\n        }\n\n        out.writeInt(stringSetValues.size());\n        for (Map.Entry<String, Map<String, String>> e : stringSetValues.entrySet()) {\n            out.writeUTF(e.getKey());\n            out.writeInt(e.getValue().size());\n            for (Map.Entry<String, String> s : e.getValue().entrySet()) {\n                out.writeUTF(s.getKey());\n                out.writeUTF(s.getValue());\n            }\n        }\n    }\n\n    @Override\n    public void readFields(DataInput in) throws IOException {\n        // String\n        int nrOfValues = in.readInt();\n        for (int count = 0; count < nrOfValues; count++) {\n            stringValues.put(in.readUTF(), in.readUTF());\n        }\n\n        // Long\n        nrOfValues = in.readInt();\n        for (int count = 0; count < nrOfValues; count++) {\n            longValues.put(in.readUTF(), in.readLong());\n        }\n\n        // Double\n        nrOfValues = in.readInt();\n        for (int count = 0; count < nrOfValues; count++) {\n            doubleValues.put(in.readUTF(), in.readDouble());\n        }\n\n        // String Prefixes\n        nrOfValues = in.readInt();\n        for (int count = 0; count < nrOfValues; count++) {\n            stringSetPrefixes.put(in.readUTF(), in.readUTF());\n        }\n\n        // String Map\n        int nrOfFields = in.readInt();\n        for (int fieldNr = 0; fieldNr < nrOfFields; fieldNr++) {\n            String fieldName = in.readUTF();\n            nrOfValues = in.readInt();\n            Map<String, String> values =  new HashMap<>(nrOfValues);\n            for (int valueNr = 0; valueNr < nrOfValues; valueNr++) {\n                String key = in.readUTF();\n                String value = in.readUTF();\n                values.put(key, value);\n            }\n            stringSetValues.put(fieldName, values);\n        }\n    }\n\n    public ParsedRecord() {\n    }\n\n    public void clear() {\n        stringValues.clear();\n        longValues.clear();\n        doubleValues.clear();\n        for (Map.Entry<String, Map<String, String>> stringMap : stringSetValues.entrySet()) {\n            stringMap.getValue().clear();\n        }\n    }\n\n    public void set(String name, String value) {\n        if (value != null) {\n            stringValues.put(name, value);\n        }\n    }\n\n    public void set(String name, Long value) {\n        if (value != null) {\n            longValues.put(name, value);\n        }\n    }\n\n    public void set(String name, Double value) {\n        if (value != null) {\n            doubleValues.put(name, value);\n        }\n    }\n\n    /**\n     * For multivalue things we need to know what the name is we are expecting.\n     * For those patterns we match the values we get against\n     *\n     * @param name the name of the requested multivalue\n     */\n    public void declareRequestedFieldname(String name) {\n        if (name.endsWith(\".*\")) {\n            stringSetValues.put(name, new HashMap<>());\n            stringSetPrefixes.put(name.substring(0, name.length() - 1), name);\n        }\n    }\n\n    public void setMultiValueString(String name, String value) {\n        if (value != null) {\n            for (Map.Entry<String, String> stringSetPrefix : stringSetPrefixes.entrySet()) {\n                String prefix = stringSetPrefix.getKey();\n                if (name.startsWith(prefix)) {\n                    stringSetValues\n                        .get(stringSetPrefix.getValue())\n                            .put(name.substring(prefix.length()), value);\n                }\n            }\n        }\n    }\n\n    public String getString(String name) {\n        return stringValues.get(name);\n    }\n\n    public Long getLong(String name) {\n        return longValues.get(name);\n    }\n\n    public Double getDouble(String name) {\n        return doubleValues.get(name);\n    }\n\n    public Map<String, String> getStringSet(String name) {\n        return stringSetValues.get(name);\n    }\n\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-inputformat/src/test/java/nl/basjes/hadoop/input/TestApacheHttpdLogfileInputFormat.java",
    "content": "package nl.basjes.hadoop.input;\n/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport nl.basjes.parse.core.Dissector;\nimport nl.basjes.parse.core.test.NormalValuesDissector;\nimport nl.basjes.parse.httpdlog.HttpdLogFormatDissector;\nimport org.apache.hadoop.conf.Configuration;\nimport org.apache.hadoop.fs.Path;\nimport org.apache.hadoop.io.LongWritable;\nimport org.apache.hadoop.mapreduce.InputFormat;\nimport org.apache.hadoop.mapreduce.RecordReader;\nimport org.apache.hadoop.mapreduce.TaskAttemptContext;\nimport org.apache.hadoop.mapreduce.TaskAttemptID;\nimport org.apache.hadoop.mapreduce.lib.input.FileSplit;\nimport org.apache.hadoop.mapreduce.task.TaskAttemptContextImpl;\nimport org.apache.hadoop.util.ReflectionUtils;\nimport org.junit.jupiter.api.Test;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\nclass TestApacheHttpdLogfileInputFormat {\n    // CHECKSTYLE.OFF: LineLength\n    final String logformat = \"%h %l %u %t \\\"%r\\\" %>s %O \\\"%{%D %F %R %T %r %a %A %b %B %C %d %G %h %H %I %j %k %l %m %M %p %S %u %Y %z}t\\\" \\\"%{User-Agent}i\\\"\";\n    // CHECKSTYLE.ON: LineLength\n\n    @Test\n    void checkInputFormat() throws IOException, InterruptedException {\n        Configuration conf = new Configuration(false);\n        conf.set(\"fs.default.name\", \"file:///\");\n\n        conf.set(\"nl.basjes.parse.apachehttpdlogline.format\", logformat);\n\n        // A ',' separated list of fields\n        conf.set(\"nl.basjes.parse.apachehttpdlogline.fields\",\n            \"TIME.EPOCH:request.receive.time.epoch,\" +\n            \"HTTP.USERAGENT:request.user-agent\");\n\n        File testFile = new File(\"src/test/resources/access.log\");\n        Path path = new Path(testFile.getAbsoluteFile().toURI());\n        FileSplit split = new FileSplit(path, 0, testFile.length(), null);\n\n        InputFormat<LongWritable, ParsedRecord> inputFormat = ReflectionUtils.newInstance(ApacheHttpdLogfileInputFormat.class, conf);\n        TaskAttemptContext context = new TaskAttemptContextImpl(conf, new TaskAttemptID());\n        RecordReader<LongWritable, ParsedRecord> reader = inputFormat.createRecordReader(split, context);\n\n        reader.initialize(split, context);\n\n        assertTrue(reader.nextKeyValue());\n\n        ParsedRecord value = reader.getCurrentValue();\n        assertEquals(\"1483272081000\", value.getString(\"TIME.EPOCH:request.receive.time.epoch\"));\n        assertEquals(\"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36\",\n            value.getString(\"HTTP.USERAGENT:request.user-agent\"));\n    }\n\n    @Test\n    void checkAllOutputTypes() throws IOException, InterruptedException {\n        Configuration conf = new Configuration(false);\n        conf.set(\"fs.default.name\", \"file:///\");\n\n        // A ',' separated list of fields\n        List<String> fields = Arrays.asList(\n                \"ANY:any\",\n                \"ANY:any\",\n                \"ANY:any\",\n                \"STRING:string\",\n                \"STRING:string\",\n                \"STRING:string\",\n                \"INT:int\",\n                \"INT:int\",\n                \"INT:int\",\n                \"LONG:long\",\n                \"LONG:long\",\n                \"LONG:long\",\n                \"FLOAT:float\",\n                \"FLOAT:float\",\n                \"FLOAT:float\",\n                \"DOUBLE:double\",\n                \"DOUBLE:double\",\n                \"DOUBLE:double\");\n\n        File testFile = new File(\"src/test/resources/access.log\");\n        Path path = new Path(testFile.getAbsoluteFile().toURI());\n        FileSplit split = new FileSplit(path, 0, testFile.length(), null);\n\n        Map<String, Set<String>> typeRemappings = new HashMap<>();\n        List<Dissector> dissectors = new ArrayList<>();\n        dissectors.add(new NormalValuesDissector(HttpdLogFormatDissector.INPUT_TYPE));\n\n        InputFormat<LongWritable, ParsedRecord> inputFormat = new ApacheHttpdLogfileInputFormat(\n            logformat,\n            fields,\n            typeRemappings,\n            dissectors);\n        TaskAttemptContext context = new TaskAttemptContextImpl(conf, new TaskAttemptID());\n        RecordReader<LongWritable, ParsedRecord> reader = inputFormat.createRecordReader(split, context);\n\n        reader.initialize(split, context);\n\n        assertTrue(reader.nextKeyValue());\n\n        ParsedRecord value = reader.getCurrentValue();\n        assertEquals(\"42\",          value.getString(\"ANY:any\"));             // any_string\n        assertEquals(42L,           value.getLong(\"ANY:any\").longValue());   // any_long\n        assertEquals(42D,           value.getDouble(\"ANY:any\"), 0.1D);       // any_double\n        assertEquals(\"FortyTwo\",    value.getString(\"STRING:string\"));       // string_string\n        assertEquals(null,          value.getLong(\"STRING:string\"));         // string_long\n        assertEquals(null,          value.getDouble(\"STRING:string\"));       // string_double\n        assertEquals(\"42\",          value.getString(\"INT:int\"));             // int_string\n        assertEquals(42L,           value.getLong(\"INT:int\").longValue());   // int_long\n        assertEquals(null,          value.getDouble(\"INT:int\"));             // int_double\n        assertEquals(\"42\",          value.getString(\"LONG:long\"));           // long_string\n        assertEquals(42L,           value.getLong(\"LONG:long\").longValue()); // long_long\n        assertEquals(null,          value.getDouble(\"LONG:long\"));           // long_double\n        assertEquals(\"42.0\",        value.getString(\"FLOAT:float\"));         // float_string\n        assertEquals(null,          value.getLong(\"FLOAT:float\"));           // float_long\n        assertEquals(42D,           value.getDouble(\"FLOAT:float\"), 0.1D);   // float_double\n        assertEquals(\"42.0\",        value.getString(\"DOUBLE:double\"));       // double_string\n        assertEquals(null,          value.getLong(\"DOUBLE:double\"));         // double_long\n        assertEquals(42D,           value.getDouble(\"DOUBLE:double\"), 0.1D);  // double_double\n    }\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-inputformat/src/test/java/nl/basjes/hadoop/input/TestGetAllFields.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.hadoop.input;\n\nimport org.apache.hadoop.io.LongWritable;\nimport org.apache.hadoop.mapreduce.RecordReader;\nimport org.junit.jupiter.api.Test;\n\nimport java.io.IOException;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\nclass TestGetAllFields {\n\n    @Test\n    void testGetAllField() throws IOException, InterruptedException {\n        ApacheHttpdLogfileInputFormat inputFormat =\n            new ApacheHttpdLogfileInputFormat(\n                \"common\",\n                Collections.singleton(ApacheHttpdLogfileRecordReader.FIELDS), Collections.emptyMap(), Collections.emptyList());\n        RecordReader<LongWritable, ParsedRecord> reader = inputFormat.getRecordReader();\n\n        Set<String> possibleFields = new HashSet<>(100);\n        while (reader.nextKeyValue()){\n            ParsedRecord currentValue = reader.getCurrentValue();\n            String value = currentValue.getString(ApacheHttpdLogfileRecordReader.FIELDS);\n            if (value == null) {\n                continue;\n            }\n            possibleFields.add(value);\n        }\n        assertTrue(possibleFields.containsAll(Arrays.asList(\n            // A subset of all fields that come out\n            \"STRING:connection.client.user\",\n            \"IP:connection.client.host.last\",\n            \"TIME.STAMP:request.receive.time.last\",\n            \"TIME.EPOCH:request.receive.time.epoch\",\n            \"STRING:request.status.last\",\n            \"STRING:connection.client.user.last\",\n            \"HTTP.METHOD:request.firstline.original.method\",\n            \"HTTP.URI:request.firstline.uri\",\n            \"HTTP.PATH:request.firstline.uri.path\",\n            \"HTTP.QUERYSTRING:request.firstline.uri.query\",\n            \"STRING:request.firstline.uri.query.*\",\n            \"BYTES:response.body.bytesclf\",\n            \"BYTESCLF:response.body.bytesclf\",\n            \"IP:connection.client.host\")));\n    }\n\n    @Test\n    void checkPossibleFields(){\n        List<String> possibleFields = new ApacheHttpdLogfileInputFormat().listPossibleFields(\"common\");\n        assertTrue(possibleFields.containsAll(Arrays.asList(\n            // A subset of all fields that come out\n            \"STRING:connection.client.user\",\n            \"IP:connection.client.host.last\",\n            \"TIME.STAMP:request.receive.time.last\",\n            \"TIME.EPOCH:request.receive.time.epoch\",\n            \"STRING:request.status.last\",\n            \"STRING:connection.client.user.last\",\n            \"HTTP.METHOD:request.firstline.original.method\",\n            \"HTTP.URI:request.firstline.uri\",\n            \"HTTP.PATH:request.firstline.uri.path\",\n            \"HTTP.QUERYSTRING:request.firstline.uri.query\",\n            \"STRING:request.firstline.uri.query.*\",\n            \"BYTES:response.body.bytesclf\",\n            \"BYTESCLF:response.body.bytesclf\",\n            \"IP:connection.client.host\")));\n    }\n\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-inputformat/src/test/java/nl/basjes/hadoop/input/TestParsedRecord.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.hadoop.input;\n\nimport org.apache.commons.io.IOUtils;\nimport org.apache.hadoop.io.Writable;\nimport org.junit.jupiter.api.Test;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.DataInputStream;\nimport java.io.DataOutputStream;\nimport java.io.IOException;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\npublic class TestParsedRecord {\n\n    // Copied from https://stackoverflow.com/questions/13288214/how-to-unit-test-hadoop-writable\n    public static byte[] serialize(Writable writable) throws IOException {\n        ByteArrayOutputStream out = new ByteArrayOutputStream();\n        DataOutputStream dataOut = null;\n        try {\n            dataOut = new DataOutputStream(out);\n            writable.write(dataOut);\n            return out.toByteArray();\n        } finally {\n            IOUtils.closeQuietly(dataOut);\n        }\n    }\n\n    public static <T extends Writable> T asWritable(byte[] bytes, Class<T> clazz) throws IOException, IllegalAccessException, InstantiationException {\n        T result;\n        DataInputStream dataIn = null;\n        try {\n            result = clazz.newInstance();\n            ByteArrayInputStream in = new ByteArrayInputStream(bytes);\n            dataIn = new DataInputStream(in);\n            result.readFields(dataIn);\n        } finally {\n            IOUtils.closeQuietly(dataIn);\n        }\n        return result;\n    }\n\n    @SuppressWarnings({\"EqualsBetweenInconvertibleTypes\", \"ObjectEqualsNull\", \"EqualsWithItself\"})\n    @Test\n    void testParsedRecordSerialization() throws IOException, InstantiationException, IllegalAccessException {\n        ParsedRecord record = new ParsedRecord();\n\n        // Set and verify\n        setAllValues(record);\n        checkAllValues(record);\n\n        byte[] serializedBytes = serialize(record);\n        ParsedRecord deserialized = asWritable(serializedBytes, ParsedRecord.class);\n\n        // Compare both before and after records\n        checkAllValues(record);\n        checkAllValues(deserialized);\n        assertTrue(record.equals(deserialized), \"Equals failed!\");\n        assertTrue(record.equals(record), \"Equals failed!\");\n        assertFalse(record.equals(null), \"Equals failed!\");\n        assertFalse(record.equals(this), \"Equals failed!\");\n        assertEquals(record.hashCode(), deserialized.hashCode(), \"Hashcode is different!\");\n        record.clear();\n    }\n\n    private void setAllValues(ParsedRecord record) {\n        record.set(\"String A\", \"42\");\n        record.set(\"String B\", \"42\");\n        record.set(\"String C\", \"42\");\n        record.set(\"String D\", \"42\");\n\n        record.set(\"Long A\", 42L);\n        record.set(\"Long B\", 42L);\n        record.set(\"Long C\", 42L);\n        record.set(\"Long D\", 42L);\n\n        record.set(\"Double A\", 42D);\n        record.set(\"Double B\", 42D);\n        record.set(\"Double C\", 42D);\n        record.set(\"Double D\", 42D);\n\n        record.declareRequestedFieldname(\"Multi_A.*\");\n        record.setMultiValueString(\"Multi_A.1\", \"Foo\");\n        record.setMultiValueString(\"Multi_A.2\", \"Bar\");\n\n        record.declareRequestedFieldname(\"Multi_B.*\");\n        record.setMultiValueString(\"Multi_B.1\", \"Foo\");\n        record.setMultiValueString(\"Multi_B.2\", \"Bar\");\n\n        record.declareRequestedFieldname(\"Multi_C.*\");\n        record.setMultiValueString(\"Multi_C.1\", \"Foo\");\n        record.setMultiValueString(\"Multi_C.2\", \"Bar\");\n    }\n\n    private void checkAllValues(ParsedRecord record) {\n        assertEquals(\"42\",          record.getString(\"String A\"),              \"String A\");\n        assertEquals(\"42\",          record.getString(\"String B\"),              \"String B\");\n        assertEquals(\"42\",          record.getString(\"String C\"),              \"String C\");\n        assertEquals(\"42\",          record.getString(\"String D\"),              \"String D\");\n        assertEquals((Long)42L,     record.getLong(\"Long A\"),                  \"Long A\");\n        assertEquals((Long)42L,     record.getLong(\"Long B\"),                  \"Long B\");\n        assertEquals((Long)42L,     record.getLong(\"Long C\"),                  \"Long C\");\n        assertEquals((Long)42L,     record.getLong(\"Long D\"),                  \"Long D\");\n        assertEquals((Double)42D,   record.getDouble(\"Double A\"),              \"Double A\");\n        assertEquals((Double)42D,   record.getDouble(\"Double B\"),              \"Double B\");\n        assertEquals((Double)42D,   record.getDouble(\"Double C\"),              \"Double C\");\n        assertEquals((Double)42D,   record.getDouble(\"Double D\"),              \"Double D\");\n\n        assertEquals(\"Foo\",         record.getStringSet(\"Multi_A.*\").get(\"1\"), \"Multi_A.* --> 1\");\n        assertEquals(\"Bar\",         record.getStringSet(\"Multi_A.*\").get(\"2\"), \"Multi_A.* --> 2\");\n\n        assertEquals(\"Foo\",         record.getStringSet(\"Multi_B.*\").get(\"1\"), \"Multi_B.* --> 1\");\n        assertEquals(\"Bar\",         record.getStringSet(\"Multi_B.*\").get(\"2\"), \"Multi_B.* --> 2\");\n\n        assertEquals(\"Foo\",         record.getStringSet(\"Multi_C.*\").get(\"1\"), \"Multi_C.* --> 1\");\n        assertEquals(\"Bar\",         record.getStringSet(\"Multi_C.*\").get(\"2\"), \"Multi_C.* --> 2\");\n    }\n\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-inputformat/src/test/resources/access.log",
    "content": "127.0.0.1 - - [01/Jan/2017:13:01:21 +0100] \"GET / HTTP/1.1\" 200 3525 \"01/01/17 2017-01-01 13:01 13:01:21 01:01:21 PM Sun Sunday Jan January 20 01 2016 Jan 13 01 001 13  1 01 01 PM 21 7 2017 +0100\" \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36\"\n"
  },
  {
    "path": "httpdlog/httpdlog-inputformat/src/test/resources/log4j.properties",
    "content": "#\n# Apache HTTPD & NGINX Access log parsing made easy\n# Copyright (C) 2011-2023 Niels Basjes\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# Root logger option\nlog4j.rootLogger=INFO, stdout\n#, file\nlog4j.appender.stdout=org.apache.log4j.ConsoleAppender\nlog4j.appender.stdout.Target=System.out\nlog4j.appender.stdout.threshold=INFO\nlog4j.appender.stdout.layout=org.apache.log4j.PatternLayout\nlog4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} [%-5p] %-40c{1}:%5L: %m%n\n\n## file appender\n#log4j.appender.file=org.apache.log4j.RollingFileAppender\n#log4j.appender.file.File=target/debug.log\n#log4j.appender.file.threshold=DEBUG\n#log4j.appender.file.layout=org.apache.log4j.PatternLayout\n#log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd} %d{ABSOLUTE} [%-5p] %-40c{1}:%5L: %m%n\n#log4j.appender.file.Append=false\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/pom.xml",
    "content": "<?xml version=\"1.0\"?>\n<!--\n Apache HTTPD & NGINX Access log parsing made easy\n Copyright (C) 2011-2023 Niels Basjes\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n https://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n-->\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n  <modelVersion>4.0.0</modelVersion>\n\n  <parent>\n    <artifactId>httpdlog</artifactId>\n    <groupId>nl.basjes.parse.httpdlog</groupId>\n    <version>6.0.1-SNAPSHOT</version>\n  </parent>\n\n  <artifactId>httpdlog-parser</artifactId>\n  <name>Parser - Apache HTTPD - Parser</name>\n  <url>https://niels.basjes.nl</url>\n\n  <properties>\n    <antlr.version>4.13.2</antlr.version>\n    <jackson.version>2.21.3</jackson.version>\n    <!-- Now decoupled ... -->\n    <jackson-annotations.version>2.21</jackson-annotations.version>\n  </properties>\n\n  <dependencies>\n\n    <dependency>\n      <groupId>nl.basjes.parse</groupId>\n      <artifactId>parser-core</artifactId>\n      <version>${project.version}</version>\n    </dependency>\n\n    <dependency>\n      <groupId>nl.basjes.parse</groupId>\n      <artifactId>parser-core</artifactId>\n      <version>${project.version}</version>\n      <classifier>tests</classifier>\n      <scope>test</scope>\n    </dependency>\n\n    <dependency>\n      <groupId>commons-codec</groupId>\n      <artifactId>commons-codec</artifactId>\n      <version>1.22.0</version>\n    </dependency>\n\n    <dependency>\n      <groupId>org.antlr</groupId>\n      <artifactId>antlr4-runtime</artifactId>\n      <version>${antlr.version}</version>\n    </dependency>\n\n    <!-- The geoip library has MIXED versions of Jackson as dependencies -->\n    <dependency>\n      <groupId>com.maxmind.geoip2</groupId>\n      <artifactId>geoip2</artifactId>\n      <version>5.1.0</version>\n      <exclusions>\n        <exclusion>\n          <groupId>commons-codec</groupId>\n          <artifactId>commons-codec</artifactId>\n        </exclusion>\n        <exclusion>\n          <groupId>com.fasterxml.jackson.core</groupId>\n          <artifactId>jackson-core</artifactId>\n        </exclusion>\n        <exclusion>\n          <groupId>com.fasterxml.jackson.core</groupId>\n          <artifactId>jackson-databind</artifactId>\n        </exclusion>\n        <exclusion>\n          <groupId>com.fasterxml.jackson.core</groupId>\n          <artifactId>jackson-annotations</artifactId>\n        </exclusion>\n      </exclusions>\n    </dependency>\n\n    <dependency>\n      <groupId>com.fasterxml.jackson.core</groupId>\n      <artifactId>jackson-core</artifactId>\n      <version>${jackson.version}</version>\n    </dependency>\n\n    <dependency>\n      <groupId>com.fasterxml.jackson.core</groupId>\n      <artifactId>jackson-databind</artifactId>\n      <version>${jackson.version}</version>\n    </dependency>\n\n    <dependency>\n      <groupId>com.fasterxml.jackson.core</groupId>\n      <artifactId>jackson-annotations</artifactId>\n      <version>${jackson-annotations.version}</version>\n    </dependency>\n\n  </dependencies>\n\n  <build>\n\n    <resources>\n      <resource>\n        <directory>src/main/resources</directory>\n        <excludes>\n          <exclude>version/*</exclude>\n        </excludes>\n      </resource>\n    </resources>\n\n    <plugins>\n      <plugin>\n        <groupId>org.jacoco</groupId>\n        <artifactId>jacoco-maven-plugin</artifactId>\n        <configuration>\n          <excludes>\n            <!-- Generated class -->\n            <exclude>nl/basjes/parse/httpdlog/Version.class</exclude>\n            <!-- Generated by ANTLR4 -->\n            <exclude>nl/basjes/parse/strftime/*.class</exclude>\n          </excludes>\n        </configuration>\n      </plugin>\n\n      <plugin>\n        <groupId>com.google.code.maven-replacer-plugin</groupId>\n        <artifactId>replacer</artifactId>\n        <version>1.5.3</version>\n        <executions>\n          <execution>\n            <phase>generate-sources</phase>\n            <goals>\n              <goal>replace</goal>\n            </goals>\n          </execution>\n        </executions>\n        <configuration>\n          <file>${basedir}/src/main/resources/version/Version.java.template</file>\n          <outputFile>${basedir}/target/generated-sources/java/nl/basjes/parse/httpdlog/Version.java</outputFile>\n          <replacements>\n            <replacement>\n              <!--suppress MavenModelInspection -->\n              <token>@git.commit.id.describe-short@</token>\n              <!--suppress MavenModelInspection -->\n              <value>${git.commit.id.describe-short}</value>\n            </replacement>\n            <replacement>\n              <token>@project.build.outputTimestamp@</token>\n              <value>${project.build.outputTimestamp}</value>\n            </replacement>\n            <replacement>\n              <token>@project.version@</token>\n              <value>${project.version}</value>\n            </replacement>\n          </replacements>\n        </configuration>\n      </plugin>\n\n      <plugin>\n        <groupId>org.codehaus.mojo</groupId>\n        <artifactId>build-helper-maven-plugin</artifactId>\n        <version>3.6.1</version>\n        <executions>\n          <execution>\n            <id>add-source</id>\n            <phase>generate-sources</phase>\n            <goals>\n              <goal>add-source</goal>\n            </goals>\n            <configuration>\n              <sources>\n                <source>${project.build.directory}/generated-sources/java/</source>\n              </sources>\n            </configuration>\n          </execution>\n        </executions>\n      </plugin>\n\n      <!-- Some of the dependencies (Antlr4) have proven to be problematic for    -->\n      <!-- downstream users who need different versions of these in the same application.    -->\n      <!-- So for only these we include and relocate the used classes into the main jar. -->\n      <plugin>\n        <groupId>org.apache.maven.plugins</groupId>\n        <artifactId>maven-shade-plugin</artifactId>\n        <configuration>\n          <minimizeJar>true</minimizeJar>\n          <filters>\n            <filter>\n              <artifact>org.antlr:antlr4-runtime</artifact>\n              <excludes>\n                <exclude>META-INF/services/**</exclude>\n                <exclude>META-INF/MANIFEST.MF</exclude>\n              </excludes>\n            </filter>\n            <filter>\n              <artifact>org.apache.commons:commons-text</artifact>\n              <excludes>\n                <exclude>META-INF/MANIFEST.MF</exclude>\n                <!-- If not excluded shading overwrites the module-info.class for Yauaa -->\n                <exclude>META-INF/versions/9/module-info.class</exclude>\n              </excludes>\n            </filter>\n            <filter>\n              <!-- Pulled in as a dependency for commons-text -->\n              <artifact>org.apache.commons:commons-lang3</artifact>\n              <excludes>\n                <exclude>META-INF/MANIFEST.MF</exclude>\n              </excludes>\n            </filter>\n          </filters>\n          <relocations>\n            <relocation>\n              <pattern>org.antlr</pattern>\n              <shadedPattern>${project.groupId}.${project.artifactId}.shaded.org.antlr</shadedPattern>\n            </relocation>\n            <relocation>\n              <pattern>org.apache.commons.text</pattern>\n              <shadedPattern>${project.groupId}.${project.artifactId}.shaded.org.apache.commons.text</shadedPattern>\n            </relocation>\n            <relocation>\n              <pattern>org.apache.commons.lang3</pattern>\n              <shadedPattern>${project.groupId}.${project.artifactId}.shaded.org.apache.commons.lang3</shadedPattern>\n            </relocation>\n          </relocations>\n        </configuration>\n\n        <executions>\n          <execution>\n            <id>inject-problematic-dependencies</id>\n            <phase>package</phase>\n            <goals>\n              <goal>shade</goal>\n            </goals>\n            <configuration>\n              <artifactSet>\n                <includes>\n                  <include>org.antlr:antlr4-runtime</include>\n                  <!-- These are shaded in because of backward compatibility problems when using logparser -->\n                  <!-- in an environment which has already loaded an older version of either of these.     -->\n                  <include>org.apache.commons:commons-text</include>\n                  <include>org.apache.commons:commons-lang3</include>\n                </includes>\n              </artifactSet>\n            </configuration>\n          </execution>\n\n        </executions>\n      </plugin>\n\n      <plugin>\n        <groupId>org.apache.maven.plugins</groupId>\n        <artifactId>maven-clean-plugin</artifactId>\n        <configuration>\n          <filesets>\n            <fileset>\n              <directory>${project.basedir}</directory>\n              <includes>\n                <include>dependency-reduced-pom.xml</include>\n              </includes>\n            </fileset>\n          </filesets>\n        </configuration>\n      </plugin>\n\n      <plugin>\n        <groupId>org.antlr</groupId>\n        <artifactId>antlr4-maven-plugin</artifactId>\n        <version>${antlr.version}</version>\n        <executions>\n          <execution>\n            <id>antlr</id>\n            <goals>\n              <goal>antlr4</goal>\n            </goals>\n          </execution>\n        </executions>\n      </plugin>\n\n    </plugins>\n  </build>\n</project>\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/main/antlr4/nl/basjes/parse/strftime/StrfTime.g4",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\ngrammar StrfTime;\n\n// Comments copied from the strftime man 3 page.\n// See: http://man7.org/linux/man-pages/man3/strftime.3.html\n\n\n// %E    Modifier: use alternative format, see below.\n// %O    Modifier: use alternative format, see below.\n\n// Some conversion specifications can be modified by preceding the\n// conversion specifier character by the E or O modifier to indicate\n// that an alternative format should be used.  If the alternative format\n// or specification does not exist for the current locale, the behavior\n// will be as if the unmodified conversion specification were used.\n// The Single UNIX Specification mentions %Ec, %EC, %Ex, %EX, %Ey, %EY,\n// %Od, %Oe, %OH, %OI, %Om, %OM, %OS, %Ou, %OU, %OV, %Ow, %OW, %Oy,\n// where the effect of the O modifier is to use alternative numeric\n// symbols (say, roman numerals), and that of the E modifier is to use a\n// locale-dependent alternative representation.\n\n// We are simply ignoring all of these modifiers\n\nfragment MOD: ( 'E' | 'O' )?;\n\nMsecFrac : '%'? 'msec_frac' ; // Apache HTTPD specific: milliseconds fraction\nUsecFrac : '%'? 'usec_frac' ; // Apache HTTPD specific: microseconds fraction\n\nPa : '%' MOD 'a' ; // The abbreviated name of the day of the week according to the current locale.\nPA : '%' MOD 'A' ; // The full name of the day of the week according to the current locale.\nPb : '%' MOD 'b' ; // The abbreviated month name according to the current locale.\nPh : '%' MOD 'h' ; // Equivalent to %b.\nPB : '%' MOD 'B' ; // The full month name according to the current locale.\nPc : '%' MOD 'c' ; // The preferred date and time representation for the current locale.\nPC : '%' MOD 'C' ; // The century number (year/100) as a 2-digit integer.\nPd : '%' MOD 'd' ; // The day of the month as a decimal number (range 01 to 31).\nPD : '%' MOD 'D' ; // Equivalent to %m/%d/%y. (Yecch—for Americans only)\nPe : '%' MOD 'e' ; // Like %d, the day of the month as a decimal number, but a leading zero is replaced by a space.\nPF : '%' MOD 'F' ; // Equivalent to %Y-%m-%d (the ISO 8601 date format).\nPG : '%' MOD 'G' ; // The ISO 8601 week-based year (see NOTES) with century as a decimal number. The 4-digit year corresponding to the ISO week number (see %V). This has the same format and value as %Y, except that if the ISO week number belongs to the previous or next year, that year is used instead.\nPg : '%' MOD 'g' ; // Like %G, but without century, that is, with a 2-digit year (00–99).\nPH : '%' MOD 'H' ; // The hour as a decimal number using a 24-hour clock (range 00 to 23).\nPI : '%' MOD 'I' ; // The hour as a decimal number using a 12-hour clock (range 01 to 12).\nPj : '%' MOD 'j' ; // The day of the year as a decimal number (range 001 to 366).\nPk : '%' MOD 'k' ; // The hour (24-hour clock) as a decimal number (range 0 to 23); single digits are preceded by a blank. (See also %H)\nPl : '%' MOD 'l' ; // The hour (12-hour clock) as a decimal number (range 1 to 12); single digits are preceded by a blank. (See also %I)\nPm : '%' MOD 'm' ; // The month as a decimal number (range 01 to 12).\nPM : '%' MOD 'M' ; // The minute as a decimal number (range 00 to 59).\nPp : '%' MOD 'p' ; // Either \"AM\" or \"PM\" according to the given time value, or the corresponding strings for the current locale. Noon is treated as \"PM\" and midnight as \"AM\".\nPP : '%' MOD 'P' ; // Like %p but in lowercase: \"am\" or \"pm\" or a corresponding string for the current locale.\nPr : '%' MOD 'r' ; // The time in a.m. or p.m. notation. In the POSIX locale this is equivalent to %I:%M:%S %p.\nPR : '%' MOD 'R' ; // The time in 24-hour notation (%H:%M). For a version including the seconds, see %T below.\nPs : '%' MOD 's' ; // The number of seconds since the Epoch, 1970-01-01 00:00:00 +0000 (UTC).\nPS : '%' MOD 'S' ; // The second as a decimal number (range 00 to 60). (The range is up to 60 to allow for occasional leap seconds)\nPT : '%' MOD 'T' ; // The time in 24-hour notation (%H:%M:%S).\nPu : '%' MOD 'u' ; // The day of the week as a decimal, range 1 to 7, Monday being 1. See also %w.\nPU : '%' MOD 'U' ; // The week number of the current year as a decimal number, range 00 to 53, starting with the first Sunday as the first day of week 01. See also %V and %W.\nPV : '%' MOD 'V' ; // The ISO 8601 week number (see NOTES) of the current year as a decimal number, range 01 to 53, where week 1 is the first week that has at least 4 days in the new year. See also %U and %W.\nPw : '%' MOD 'w' ; // The day of the week as a decimal, range 0 to 6, Sunday being 0. See also %u.\nPW : '%' MOD 'W' ; // The week number of the current year as a decimal number, range 00 to 53, starting with the first Monday as the first day of week 01.\nPx : '%' MOD 'x' ; // The preferred date representation for the current locale without the time.\nPX : '%' MOD 'X' ; // The preferred time representation for the current locale without the date.\nPy : '%' MOD 'y' ; // The year as a decimal number without a century (range 00 to 99).\nPY : '%' MOD 'Y' ; // The year as a decimal number including the century.\nPz : '%' MOD 'z' ; // The +hhmm or -hhmm numeric timezone.\nPZ : '%' MOD 'Z' ; // The timezone name or abbreviation.\nPplus : '%' MOD '+' ; // The date and time in date(1) format.\n\nPt : '%t' ; // A tab character.\nPn : '%n' ; // A newline character.\nPERCENT : '%%' ; // A literal '%' character.\n\nLITERAL : ~('%');\n\npattern\n    : (literal|token)+\n    ;\n\nliteral\n    : text\n    | tab\n    | percent\n    | newline\n    ;\n\ntext: LITERAL\n    ;\n\ntab : Pt\n    ;\n\npercent\n    : PERCENT\n    ;\n\nnewline\n    : Pn\n    ;\n\ntoken\n    : pa | pA | pb | pB | pc | pC | pd | pD | pe | pF\n    | pG | pg | pH | pI | pj | pk | pl | pm | pM\n    | pp | pP | pr | pR | ps | pS | pT | pu | pU | pV\n    | pw | pW | px | pX | py | pY | pz | pZ | pplus\n    | msecFrac | usecFrac ;\n\nmsecFrac : MsecFrac ; // Apache HTTPD specific: milliseconds fraction\nusecFrac : UsecFrac ; // Apache HTTPD specific: microseconds fraction\n\npa    : Pa    ;\npA    : PA    ;\npb    : Pb | Ph ;\npB    : PB    ;\npc    : Pc    ;\npC    : PC    ;\npd    : Pd    ;\npD    : PD    ;\npe    : Pe    ;\npF    : PF    ;\npG    : PG    ;\npg    : Pg    ;\n\npH    : PH    ;\npI    : PI    ;\npj    : Pj    ;\npk    : Pk    ;\npl    : Pl    ;\npm    : Pm    ;\npM    : PM    ;\npp    : Pp    ;\npP    : PP    ;\npr    : Pr    ;\npR    : PR    ;\nps    : Ps    ;\npS    : PS    ;\npT    : PT    ;\npu    : Pu    ;\npU    : PU    ;\npV    : PV    ;\npw    : Pw    ;\npW    : PW    ;\npx    : Px    ;\npX    : PX    ;\npy    : Py    ;\npY    : PY    ;\npz    : Pz    ;\npZ    : PZ    ;\npplus : Pplus ;\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/main/assembly/job.xml",
    "content": "<!--\n Apache HTTPD & NGINX Access log parsing made easy\n Copyright (C) 2011-2023 Niels Basjes\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n https://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n-->\n<assembly\n  xmlns=\"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2\"\n  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n  xsi:schemaLocation=\"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd\">\n  <id>job</id>\n  <formats>\n    <format>jar</format>\n  </formats>\n  <includeBaseDirectory>false</includeBaseDirectory>\n  <dependencySets>\n    <dependencySet>\n      <useProjectArtifact>false</useProjectArtifact>\n      <outputDirectory>lib</outputDirectory>\n      <unpack>false</unpack>\n      <scope>compile</scope>\n    </dependencySet>\n    <!--\n    <dependencySet>\n      <useProjectArtifact>false</useProjectArtifact>\n      <outputDirectory>lib</outputDirectory>\n      <unpack>false</unpack>\n      <scope>provided</scope>\n    </dependencySet>\n    -->\n  </dependencySets>\n  <fileSets>\n    <fileSet>\n      <directory>${project.build.outputDirectory}</directory>\n      <outputDirectory>${file.separator}</outputDirectory>\n    </fileSet>\n  </fileSets>\n</assembly>\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/main/java/nl/basjes/parse/httpdlog/ApacheHttpdLogFormatDissector.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.httpdlog;\n\nimport nl.basjes.parse.core.Casts;\nimport nl.basjes.parse.httpdlog.dissectors.HttpFirstLineDissector;\nimport nl.basjes.parse.httpdlog.dissectors.StrfTimeStampDissector;\nimport nl.basjes.parse.httpdlog.dissectors.tokenformat.NamedTokenParser;\nimport nl.basjes.parse.httpdlog.dissectors.tokenformat.ParameterizedTokenParser;\nimport nl.basjes.parse.httpdlog.dissectors.tokenformat.TokenFormatDissector;\nimport nl.basjes.parse.httpdlog.dissectors.tokenformat.TokenOutputField;\nimport nl.basjes.parse.httpdlog.dissectors.tokenformat.TokenParser;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.util.ArrayList;\nimport java.util.EnumSet;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport static nl.basjes.parse.core.Casts.STRING_ONLY;\nimport static nl.basjes.parse.core.Casts.STRING_OR_LONG;\nimport static nl.basjes.parse.httpdlog.dissectors.tokenformat.TokenParser.FORMAT_CLF_HEXNUMBER;\nimport static nl.basjes.parse.httpdlog.dissectors.tokenformat.TokenParser.FORMAT_CLF_IP;\nimport static nl.basjes.parse.httpdlog.dissectors.tokenformat.TokenParser.FORMAT_CLF_NUMBER;\nimport static nl.basjes.parse.httpdlog.dissectors.tokenformat.TokenParser.FORMAT_NO_SPACE_STRING;\nimport static nl.basjes.parse.httpdlog.dissectors.tokenformat.TokenParser.FORMAT_NUMBER;\nimport static nl.basjes.parse.httpdlog.dissectors.tokenformat.TokenParser.FORMAT_STANDARD_TIME_US;\nimport static nl.basjes.parse.httpdlog.dissectors.tokenformat.TokenParser.FORMAT_STRING;\n\n@SuppressWarnings({\n    \"PMD.LongVariable\", // I like my variable names this way\n    \"PMD.CyclomaticComplexity\", \"PMD.OnlyOneReturn\",\n    \"PMD.BeanMembersShouldSerialize\", // No beans here\n    \"PMD.DataflowAnomalyAnalysis\" // Results in a lot of mostly useless messages.\n    })\npublic class ApacheHttpdLogFormatDissector extends TokenFormatDissector {\n\n    private static final Logger LOG = LoggerFactory.getLogger(ApacheHttpdLogFormatDissector.class);\n\n    public ApacheHttpdLogFormatDissector(final String logFormat) {\n        super(logFormat);\n        setInputType(HttpdLogFormatDissector.INPUT_TYPE);\n    }\n\n    public ApacheHttpdLogFormatDissector() {\n        super();\n        setInputType(HttpdLogFormatDissector.INPUT_TYPE);\n    }\n\n    private void overrideLogFormat(String originalLogformat, String logformat){\n        LOG.debug(\"Specified logformat \\\"{}\\\" was mapped to {}\", originalLogformat, logformat);\n        super.setLogFormat(logformat);\n    }\n\n    @Override\n    public void setLogFormat(final String logformat) {\n        // Commonly used logformats as documented in the manuals of the Apache Httpd\n        // LogFormat \"%h %l %u %t \\\"%r\\\" %>s %b\" common\n        // LogFormat \"%h %l %u %t \\\"%r\\\" %>s %b \\\"%{Referer}i\\\" \\\"%{User-Agent}i\\\"\" combined\n        // LogFormat \"%h %l %u %t \\\"%r\\\" %>s %b \\\"%{Referer}i\\\" \\\"%{User-Agent}i\\\" %I %O\" combinedio\n        // LogFormat \"%{Referer}i -> %U\" referer\n        // LogFormat \"%{User-agent}i\" agent\n\n        switch (logformat.toLowerCase(Locale.getDefault())) {\n            case \"common\":\n                overrideLogFormat(logformat, \"%h %l %u %t \\\"%r\\\" %>s %b\");\n                break;\n            case \"combined\":\n                overrideLogFormat(logformat, \"%h %l %u %t \\\"%r\\\" %>s %b \\\"%{Referer}i\\\" \\\"%{User-Agent}i\\\"\");\n                break;\n            case \"combinedio\":\n                overrideLogFormat(logformat, \"%h %l %u %t \\\"%r\\\" %>s %b \\\"%{Referer}i\\\" \\\"%{User-Agent}i\\\" %I %O\");\n                break;\n            case \"referer\":\n                overrideLogFormat(logformat, \"%{Referer}i -> %U\");\n                break;\n            case \"agent\":\n                overrideLogFormat(logformat, \"%{User-agent}i\");\n                break;\n            default:\n                super.setLogFormat(logformat);\n                break;\n        }\n    }\n\n    public static boolean looksLikeApacheFormat(String logFormat) {\n        if (logFormat.indexOf('%') != -1) {\n            return true;\n        }\n        switch (logFormat.toLowerCase(Locale.getDefault())) {\n            case \"common\":\n            case \"combined\":\n            case \"combinedio\":\n            case \"referer\":\n            case \"agent\":\n                return true;\n            default:\n                return false;\n        }\n    }\n\n    // --------------------------------------------\n\n    protected String makeHeaderNamesLowercaseInLogFormat(String logformat) {\n        // In vim I would simply do: %s@{\\([^}]*\\)}@{\\L\\1\\E@g\n        // But such an expression is not (yet) possible in Java\n        StringBuffer sb = new StringBuffer(logformat.length());\n\n        // All patterns that have a 'name' (note we do NOT do it to %{...}t )\n        Pattern p = Pattern.compile(\"%\\\\{([^}]*)}([^t])\");\n        Matcher m = p.matcher(logformat);\n        while (m.find()) {\n            m.appendReplacement(sb, \"%{\"+m.group(1).toLowerCase()+'}'+m.group(2));\n        }\n        m.appendTail(sb);\n\n        return sb.toString();\n    }\n\n    protected String removeModifiersFromLogformat(String tokenLogFormat) {\n        // Modifiers\n        // Particular items can be restricted to print only for responses with specific HTTP status codes\n        // by placing a comma-separated list of status codes immediately following the \"%\".\n        // The status code list may be preceded by a \"!\" to indicate negation.\n        //\n        // %400,501{User-agent}i     Logs User-agent on 400 errors and 501 errors only.\n        //                           For other status codes, the literal string \"-\" will be logged.\n        // %!200,304,302{Referer}i   Logs Referer on all requests that do not return one of the three\n        //                           specified codes, \"-\" otherwise.\n\n        return tokenLogFormat.replaceAll(\"%!?[0-9]{3}(?:,[0-9]{3})*\", \"%\");\n    }\n\n    protected String fixTimestampFormat(String tokenLogFormat) {\n        // The %t is mapped to the actual time format surrounded by '[' ']'\n        // We generate the [] around it and in the rest of the parsing\n        // work with the clean format.\n        // This is mainly needed to ensure reuse in conjunction with Nginx parsing.\n        // NOTE: The %{...}t time format does NOT get the automatic '[' ']' around it.\n        return tokenLogFormat.replaceAll(\"%t\", \"[%t]\");\n\n    }\n\n    @Override\n    protected String cleanupLogFormat(String tokenLogFormat) {\n        String result = removeModifiersFromLogformat(tokenLogFormat);\n        result =  makeHeaderNamesLowercaseInLogFormat(result);\n        result = fixTimestampFormat(result);\n        return result;\n    }\n\n    @Override\n    public String decodeExtractedValue(String tokenName, String value) {\n        if (value == null || value.equals(\"\")) {\n            return value;\n        }\n\n        // In Apache logfiles a '-' means a 'not specified' / 'empty' value.\n        if (value.equals(\"-\")){\n            return null;\n        }\n\n        // http://httpd.apache.org/docs/current/mod/mod_log_config.html#formats\n        // Format Notes\n        // For security reasons, starting with version 2.0.46, non-printable and other special characters\n        // in %r, %i and %o are escaped using \\xhh sequences, where hh stands for the hexadecimal representation of\n        // the raw byte. Exceptions from this rule are \" and \\, which are escaped by prepending a backslash, and\n        // all whitespace characters, which are written in their C-style notation (\\n, \\t, etc).\n        // In versions prior to 2.0.46, no escaping was performed on these strings so you had to be quite careful\n        // when dealing with raw log files.\n\n        if (tokenName.equals(\"request.firstline\")       ||  // %r         First line of request.\n            tokenName.equals(\"request.user-agent\")      ||  // %{User-Agent}i The contents of the User-Agent request header.\n            tokenName.equals(\"request.user-agent.last\") ||  // %{User-Agent}i The contents of the User-Agent request header.\n            tokenName.startsWith(\"request.header.\")     ||  // %{Foobar}i The contents of Foobar: request header line(s).\n            tokenName.startsWith(\"response.header.\"))   {   // %{Foobar}o The contents of Foobar: response header line(s).\n            return Utils.decodeApacheHTTPDLogValue(value);\n        }\n\n        return value;\n    }\n\n    // --------------------------------------------\n    @Override\n    protected List<TokenParser> createAllTokenParsers() {\n        List<TokenParser> parsers = new ArrayList<>(60);\n\n        // Quote from\n        // http://httpd.apache.org/docs/current/mod/mod_log_config.html#logformat\n        // Format String Description\n        // -------\n        // %% The percent sign\n        parsers.add(new FixedStringTokenParser(\"%%\", \"%\"));\n\n        // -------\n        // %a Remote IP-address\n        parsers.addAll(createFirstAndLastTokenParsers(\"%a\",\n                \"connection.client.ip\", \"IP\",\n                STRING_ONLY, FORMAT_CLF_IP));\n\n        // %{c}a Underlying peer IP address of the connection (see the mod_remoteip module).\n        parsers.addAll(createFirstAndLastTokenParsers(\"%{c}a\",\n                \"connection.client.peerip\", \"IP\",\n                STRING_ONLY, FORMAT_CLF_IP));\n\n        // -------\n        // %A Local IP-address\n        parsers.addAll(createFirstAndLastTokenParsers(\"%A\",\n                \"connection.server.ip\", \"IP\",\n                STRING_ONLY, FORMAT_CLF_IP));\n\n        // -------\n        // %B Size of response in bytes, excluding HTTP headers.\n        parsers.addAll(createFirstAndLastTokenParsers(\"%B\",\n                \"response.body.bytes\", \"BYTES\",\n                STRING_OR_LONG, FORMAT_NUMBER));\n\n        // -------\n        // %b Size of response in bytes, excluding HTTP headers. In CLF format,\n        // i.e. a '-' rather than a 0 when no bytes are sent.\n        parsers.addAll(createFirstAndLastTokenParsers(\"%b\",\n            \"response.body.bytes\", \"BYTESCLF\",\n            STRING_OR_LONG, FORMAT_CLF_NUMBER));\n\n        // Additional support for the deprecated old output\n        addExtraOutput(parsers, \"%b\",\n            new TokenOutputField(\"BYTES\", \"response.body.bytesclf\", STRING_OR_LONG)\n            .deprecateFor(\"BYTESCLF:response.body.bytes\"));\n\n        // -------\n        // %{Foobar}C The contents of cookie Foobar in the request sent to the server.\n        parsers.add(new NamedTokenParser(\"\\\\%\\\\{([a-z0-9\\\\-_]*)\\\\}C\",\n                \"request.cookies.\", \"HTTP.COOKIE\",\n                STRING_ONLY, FORMAT_STRING));\n\n        // -------\n        // %{FOOBAR}e The contents of the environment variable FOOBAR\n        parsers.add(new NamedTokenParser(\"\\\\%\\\\{([a-z0-9\\\\-_]*)\\\\}e\", \"server.environment.\", \"VARIABLE\",\n                STRING_ONLY, FORMAT_STRING));\n\n        // -------\n        // %f Filename\n        parsers.addAll(createFirstAndLastTokenParsers(\"%f\",\n                \"server.filename\", \"FILENAME\",\n                STRING_ONLY, FORMAT_STRING));\n        // -------\n\n        // %h Remote host\n        parsers.addAll(createFirstAndLastTokenParsers(\"%h\",\n                \"connection.client.host\", \"IP\",\n                STRING_ONLY, FORMAT_NO_SPACE_STRING));\n\n        // -------\n        // %H The request protocol\n        parsers.addAll(createFirstAndLastTokenParsers(\"%H\",\n                \"request.protocol\", \"PROTOCOL\",\n                STRING_ONLY, FORMAT_NO_SPACE_STRING));\n\n        // -------\n        // %{Foobar}i The contents of Foobar: header line(s) in the request sent\n        // to the server. Changes made by other modules (e.g. mod_headers)\n        // affect this.\n        parsers.add(new NamedTokenParser(\"\\\\%\\\\{([a-z0-9\\\\-_]*)\\\\}i\",\n                \"request.header.\", \"HTTP.HEADER\",\n                STRING_ONLY, FORMAT_STRING));\n\n        // -------\n        // %{VARNAME}^ti The contents of VARNAME: trailer line(s) in the request sent to the server.\n        parsers.add(new NamedTokenParser(\"\\\\%\\\\{([a-z0-9\\\\-_]*)\\\\}\\\\^ti\",\n                \"request.trailer.\", \"HTTP.TRAILER\",\n                STRING_ONLY, FORMAT_STRING));\n\n        // -------\n        // %k Number of keepalive requests handled on this connection.\n        // Interesting if KeepAlive is being used, so that, for example,\n        // a '1' means the first keepalive request after the initial one, '\n        // 2' the second, etc...;\n        // otherwise this is always 0 (indicating the initial request).\n        // Available in versions 2.2.11 and later.\n        parsers.addAll(createFirstAndLastTokenParsers(\"%k\",\n                \"connection.keepalivecount\", \"NUMBER\",\n                STRING_OR_LONG, FORMAT_NUMBER));\n\n        // -------\n        // %l Remote logname (from identd, if supplied). This will return a dash\n        // unless mod_ident is present and IdentityCheck is set On.\n        parsers.addAll(createFirstAndLastTokenParsers(\"%l\",\n                \"connection.client.logname\", \"NUMBER\",\n                STRING_OR_LONG, FORMAT_CLF_NUMBER));\n\n        // -------\n        // %L The request log ID from the error log (or '-' if nothing has been logged to the error log for this request).\n        // Look for the matching error log line to see what request caused what error.\n        parsers.addAll(createFirstAndLastTokenParsers(\"%L\",\n                \"request.errorlogid\", \"STRING\",\n                STRING_ONLY, FORMAT_NO_SPACE_STRING));\n\n        // -------\n        // %m The request method\n        parsers.addAll(createFirstAndLastTokenParsers(\"%m\",\n                \"request.method\", \"HTTP.METHOD\",\n                STRING_ONLY, FORMAT_NO_SPACE_STRING));\n\n        // -------\n        // %{Foobar}n The contents of note Foobar from another module.\n        parsers.add(new NamedTokenParser(\"\\\\%\\\\{([a-z0-9\\\\-_]*)\\\\}n\",\n                \"server.module_note.\", \"STRING\",\n                STRING_ONLY, FORMAT_STRING));\n\n        // -------\n        // %{Foobar}o The contents of Foobar: header line(s) in the response.\n        parsers.add(new NamedTokenParser(\"\\\\%\\\\{([a-z0-9\\\\-]*)\\\\}o\",\n                \"response.header.\", \"HTTP.HEADER\",\n                STRING_ONLY, FORMAT_STRING));\n\n        // -------\n        // %{VARNAME}^to The contents of VARNAME: trailer line(s) in the response sent from the server.\n        parsers.add(new NamedTokenParser(\"\\\\%\\\\{([a-z0-9\\\\-_]*)\\\\}\\\\^to\",\n                \"response.trailer.\", \"HTTP.TRAILER\",\n                STRING_ONLY, FORMAT_STRING));\n\n        // -------\n        // %p The canonical port of the server serving the request\n        parsers.addAll(createFirstAndLastTokenParsers(\"%p\",\n                \"request.server.port.canonical\", \"PORT\",\n                STRING_OR_LONG, FORMAT_NUMBER));\n\n        // -------\n        // %{format}p The canonical port of the server serving the request or\n        // the server's actual port or the client's actual port. Valid formats\n        // are canonical, local, or remote.\n        parsers.addAll(createFirstAndLastTokenParsers(\"%{canonical}p\",\n                \"connection.server.port.canonical\", \"PORT\",\n                STRING_OR_LONG, FORMAT_NUMBER));\n\n        parsers.addAll(createFirstAndLastTokenParsers(\"%{local}p\",\n                \"connection.server.port\", \"PORT\",\n                STRING_OR_LONG, FORMAT_NUMBER));\n\n        parsers.addAll(createFirstAndLastTokenParsers(\"%{remote}p\",\n                \"connection.client.port\", \"PORT\",\n                STRING_OR_LONG, FORMAT_NUMBER));\n\n        // -------\n        // %P The process ID of the child that serviced the request.\n        parsers.addAll(createFirstAndLastTokenParsers(\"%P\",\n                \"connection.server.child.processid\", \"NUMBER\",\n                STRING_OR_LONG, FORMAT_NUMBER));\n\n        // -------\n        // %{format}P The process ID or thread id of the child that serviced the\n        // request. Valid formats are pid, tid, and hextid. hextid requires\n        // APR 1.2.0 or higher.\n        parsers.addAll(createFirstAndLastTokenParsers(\"%{pid}P\",\n                \"connection.server.child.processid\", \"NUMBER\",\n                STRING_OR_LONG, FORMAT_NUMBER));\n\n        parsers.addAll(createFirstAndLastTokenParsers(\"%{tid}P\",\n                \"connection.server.child.threadid\", \"NUMBER\",\n                STRING_OR_LONG, FORMAT_NUMBER));\n\n        parsers.addAll(createFirstAndLastTokenParsers(\"%{hextid}P\",\n                \"connection.server.child.hexthreadid\", \"NUMBER\",\n                STRING_OR_LONG, FORMAT_CLF_HEXNUMBER));\n\n        // -------\n        // %q The query string (prepended with a ? if a query string exists,\n        // otherwise an empty string)\n        parsers.addAll(createFirstAndLastTokenParsers(\"%q\",\n                \"request.querystring\", \"HTTP.QUERYSTRING\",\n                STRING_ONLY, FORMAT_NO_SPACE_STRING));\n\n        // -------\n        // %r First line of request\n        parsers.addAll(createFirstAndLastTokenParsers(\"%r\",\n                \"request.firstline\", \"HTTP.FIRSTLINE\",\n                STRING_ONLY, HttpFirstLineDissector.FIRSTLINE_REGEX));\n\n        // -------\n        // %R The handler generating the response (if any).\n        parsers.addAll(createFirstAndLastTokenParsers(\"%R\",\n                \"request.handler\", \"STRING\",\n                STRING_ONLY, FORMAT_STRING));\n\n        // -------\n        // %s Status. For requests that got internally redirected, this is the\n        // status of the *original* request --- %>s for the last.\n        parsers.addAll(createFirstAndLastTokenParsers(\"%s\",\n            \"request.status\", \"STRING\",\n            STRING_ONLY, FORMAT_NO_SPACE_STRING, 0));\n\n        // -------\n        // %t Time the request was received (standard english format)\n        parsers.addAll(createFirstAndLastTokenParsers(\"%t\",\n                \"request.receive.time\", \"TIME.STAMP\",\n                STRING_ONLY, FORMAT_STANDARD_TIME_US));\n\n        // %{format}t The time, in the form given by format, which should be in\n        // strftime(3) format. (potentially localized)\n        parsers.add(new ParameterizedTokenParser(\"\\\\%\\\\{([^\\\\}]*%[^\\\\}]*)\\\\}t\",\n            \"request.receive.time\", \"TIME.STRFTIME_\",\n            STRING_ONLY, FORMAT_STRING,\n            -1, new StrfTimeStampDissector())\n            .setWarningMessageWhenUsed(\"Only some parts of localized timestamps are supported\")\n        );\n\n        // If the format starts with begin: (default) the time is taken at the beginning of the request processing.\n        // If it starts with end: it is the time when the log entry gets written, close to the end of the request processing.\n\n        parsers.add(new ParameterizedTokenParser(\"\\\\%\\\\{begin:([^\\\\}]*%[^\\\\}]*)\\\\}t\",\n            \"request.receive.time.begin\", \"TIME.STRFTIME_\",\n            STRING_ONLY, FORMAT_STRING,\n            0, new StrfTimeStampDissector())\n            .setWarningMessageWhenUsed(\"Only some parts of localized timestamps are supported\")\n        );\n\n        parsers.add(new ParameterizedTokenParser(\"\\\\%\\\\{end:([^\\\\}]*%[^\\\\}]*)\\\\}t\",\n            \"request.receive.time.end\", \"TIME.STRFTIME_\",\n            STRING_ONLY, FORMAT_STRING,\n            0, new StrfTimeStampDissector())\n            .setWarningMessageWhenUsed(\"Only some parts of localized timestamps are supported\")\n        );\n\n        // In addition to the formats supported by strftime(3), the following format tokens are supported:\n        // sec number of seconds since the Epoch\n        // msec number of milliseconds since the Epoch\n        // usec number of microseconds since the Epoch\n        // msec_frac millisecond fraction\n        // usec_frac microsecond fraction\n        // These tokens can not be combined with each other or strftime(3) formatting in the same format string.\n        // You can use multiple %{format}t tokens instead.\n\n        // sec\n        parsers.addAll(createFirstAndLastTokenParsers(\"%{sec}t\",\n            \"request.receive.time.sec\", \"TIME.SECONDS\",\n            STRING_OR_LONG, FORMAT_NUMBER));\n\n        parsers.addAll(createFirstAndLastTokenParsers(\"%{begin:sec}t\",\n            \"request.receive.time.begin.sec\", \"TIME.SECONDS\",\n            STRING_OR_LONG, FORMAT_NUMBER));\n\n        parsers.addAll(createFirstAndLastTokenParsers(\"%{end:sec}t\",\n            \"request.receive.time.end.sec\", \"TIME.SECONDS\",\n            STRING_OR_LONG, FORMAT_NUMBER));\n\n        // msec\n        parsers.addAll(createFirstAndLastTokenParsers(\"%{msec}t\",\n                \"request.receive.time.msec\", \"TIME.EPOCH\",\n                STRING_OR_LONG, FORMAT_NUMBER));\n\n        // Additional support for the deprecated old output\n        addExtraOutput(parsers, \"%{msec}t\",\n            new TokenOutputField(\"TIME.EPOCH\", \"request.receive.time.begin.msec\", STRING_OR_LONG)\n                .deprecateFor(\"TIME.EPOCH:request.receive.time.msec\"));\n\n        parsers.addAll(createFirstAndLastTokenParsers(\"%{begin:msec}t\",\n                \"request.receive.time.begin.msec\", \"TIME.EPOCH\",\n                STRING_OR_LONG, FORMAT_NUMBER));\n\n        parsers.addAll(createFirstAndLastTokenParsers(\"%{end:msec}t\",\n                \"request.receive.time.end.msec\", \"TIME.EPOCH\",\n                STRING_OR_LONG, FORMAT_NUMBER));\n\n        // usec\n        parsers.addAll(createFirstAndLastTokenParsers(\"%{usec}t\",\n                \"request.receive.time.usec\", \"TIME.EPOCH.USEC\",\n                STRING_OR_LONG, FORMAT_NUMBER));\n\n        // Additional support for the deprecated old output\n        addExtraOutput(parsers, \"%{usec}t\",\n            new TokenOutputField(\"TIME.EPOCH.USEC\", \"request.receive.time.begin.usec\", STRING_OR_LONG)\n                .deprecateFor(\"TIME.EPOCH.USEC:request.receive.time.usec\"));\n\n        parsers.addAll(createFirstAndLastTokenParsers(\"%{begin:usec}t\",\n                \"request.receive.time.begin.usec\", \"TIME.EPOCH.USEC\",\n                STRING_OR_LONG, FORMAT_NUMBER));\n\n        parsers.addAll(createFirstAndLastTokenParsers(\"%{end:usec}t\",\n                \"request.receive.time.end.usec\", \"TIME.EPOCH.USEC\",\n                STRING_OR_LONG, FORMAT_NUMBER));\n\n        // msec_frac\n        parsers.addAll(createFirstAndLastTokenParsers(\"%{msec_frac}t\",\n                \"request.receive.time.msec_frac\", \"TIME.EPOCH\",\n                STRING_OR_LONG, FORMAT_NUMBER));\n\n        addExtraOutput(parsers, \"%{msec_frac}t\",\n            new TokenOutputField(\"TIME.EPOCH\", \"request.receive.time.begin.msec_frac\", STRING_OR_LONG)\n                .deprecateFor(\"TIME.EPOCH:request.receive.time.msec_frac\"));\n\n        parsers.addAll(createFirstAndLastTokenParsers(\"%{begin:msec_frac}t\",\n                \"request.receive.time.begin.msec_frac\", \"TIME.EPOCH\",\n                STRING_OR_LONG, FORMAT_NUMBER));\n\n        parsers.addAll(createFirstAndLastTokenParsers(\"%{end:msec_frac}t\",\n                \"request.receive.time.end.msec_frac\", \"TIME.EPOCH\",\n                STRING_OR_LONG, FORMAT_NUMBER));\n\n        // usec_frac\n        parsers.addAll(createFirstAndLastTokenParsers(\"%{usec_frac}t\",\n                \"request.receive.time.usec_frac\", \"TIME.EPOCH.USEC_FRAC\",\n                STRING_OR_LONG, FORMAT_NUMBER));\n\n        // Additional support for the deprecated old output\n        addExtraOutput(parsers, \"%{usec_frac}t\",\n            new TokenOutputField(\"TIME.EPOCH.USEC_FRAC\", \"request.receive.time.begin.usec_frac\", STRING_OR_LONG)\n                .deprecateFor(\"TIME.EPOCH.USEC_FRAC:request.receive.time.usec_frac\"));\n\n        parsers.addAll(createFirstAndLastTokenParsers(\"%{begin:usec_frac}t\",\n                \"request.receive.time.begin.usec_frac\", \"TIME.EPOCH.USEC_FRAC\",\n                STRING_OR_LONG, FORMAT_NUMBER));\n\n        parsers.addAll(createFirstAndLastTokenParsers(\"%{end:usec_frac}t\",\n                \"request.receive.time.end.usec_frac\", \"TIME.EPOCH.USEC_FRAC\",\n                STRING_OR_LONG, FORMAT_NUMBER));\n\n        // -------\n        // %T The time taken to serve the request, in seconds.\n        parsers.addAll(createFirstAndLastTokenParsers(\"%T\",\n                \"response.server.processing.time\", \"SECONDS\",\n                STRING_OR_LONG, FORMAT_NUMBER));\n\n        // -------\n        // %D The time taken to serve the request, in microseconds.\n        parsers.addAll(createFirstAndLastTokenParsers(\"%D\",\n            \"response.server.processing.time\", \"MICROSECONDS\",\n            STRING_OR_LONG, FORMAT_NUMBER));\n\n        // Additional support for the deprecated old output\n        addExtraOutput(parsers, \"%D\",\n            new TokenOutputField(\"MICROSECONDS\", \"server.process.time\", STRING_OR_LONG)\n            .deprecateFor(\"MICROSECONDS:response.server.processing.time\"));\n\n        // -------\n        // %{UNIT}T The time taken to serve the request, in a time unit given by UNIT.\n        // Valid units are ms for milliseconds, us for microseconds, and s for seconds.\n        // Using s gives the same result as %T without any format;\n        // Using us gives the same result as %D.\n        parsers.addAll(createFirstAndLastTokenParsers(\"%{us}T\",\n            \"response.server.processing.time\", \"MICROSECONDS\",\n            STRING_OR_LONG, FORMAT_NUMBER));\n        parsers.addAll(createFirstAndLastTokenParsers(\"%{ms}T\",\n            \"response.server.processing.time\", \"MILLISECONDS\",\n            STRING_OR_LONG, FORMAT_NUMBER));\n        parsers.addAll(createFirstAndLastTokenParsers(\"%{s}T\",\n            \"response.server.processing.time\", \"SECONDS\",\n            STRING_OR_LONG, FORMAT_NUMBER));\n\n        // -------\n        // %u Remote user (from auth; may be bogus if return status (%s) is 401)\n        parsers.addAll(createFirstAndLastTokenParsers(\"%u\",\n                \"connection.client.user\", \"STRING\",\n                STRING_ONLY, FORMAT_NO_SPACE_STRING));\n\n        // -------\n        // %U The URL path requested, not including any query string.\n        parsers.addAll(createFirstAndLastTokenParsers(\"%U\",\n                \"request.urlpath\", \"URI\",\n                STRING_ONLY, FORMAT_NO_SPACE_STRING));\n\n        // -------\n        // %v The canonical ServerName of the server serving the request.\n        parsers.addAll(createFirstAndLastTokenParsers(\"%v\",\n                \"connection.server.name.canonical\", \"STRING\",\n                STRING_ONLY,  FORMAT_NO_SPACE_STRING));\n\n        // -------\n        // %V The server name according to the UseCanonicalName setting.\n        parsers.addAll(createFirstAndLastTokenParsers(\"%V\",\n                \"connection.server.name\", \"STRING\",\n                STRING_ONLY, FORMAT_NO_SPACE_STRING));\n\n        // -------\n        // %X Connection status when response is completed:\n        // X = connection aborted before the response completed.\n        // + = connection may be kept alive after the response is sent.\n        // - = connection will be closed after the response is sent.\n        // (This directive was %c in late versions of Apache 1.3, but this\n        // conflicted with the historical ssl %{var}c syntax.)\n        parsers.addAll(createFirstAndLastTokenParsers(\"%X\",\n                \"response.connection.status\", \"HTTP.CONNECTSTATUS\",\n                STRING_ONLY, FORMAT_NO_SPACE_STRING));\n\n        // -------\n        // %I Bytes received, including request and headers, cannot be zero. You\n        // need to enable mod_logio to use this.\n        // NOTE: In reality this CAN ben 0 (in case of HTTP 408 error code)\n        parsers.addAll(createFirstAndLastTokenParsers(\"%I\",\n                \"request.bytes\", \"BYTES\",\n                STRING_OR_LONG, FORMAT_CLF_NUMBER));\n\n        // -------\n        // %O Bytes sent, including headers, cannot be zero. You need to enable\n        // mod_logio to use this.\n        // NOTE: In reality this CAN ben 0 (in case of HTTP 408 error code)\n        parsers.addAll(createFirstAndLastTokenParsers(\"%O\",\n                \"response.bytes\", \"BYTES\",\n                STRING_OR_LONG, FORMAT_CLF_NUMBER));\n\n        // -------\n        // %S Bytes transferred (received and sent), including request and headers, cannot be zero.\n        // This is the combination of %I and %O. You need to enable mod_logio to use this.\n        parsers.addAll(createFirstAndLastTokenParsers(\"%S\",\n                \"total.bytes\", \"BYTES\",\n                STRING_OR_LONG, TokenParser.FORMAT_NON_ZERO_NUMBER));\n\n        // Some explicit type overrides.\n        // The '1' at the end indicates this is more important than the default TokenParser (which has an implicit 0).\n        parsers.addAll(createFirstAndLastTokenParsers(\"%{cookie}i\",\n                \"request.cookies\",    \"HTTP.COOKIES\",\n                STRING_ONLY, FORMAT_STRING, 1));\n        parsers.addAll(createFirstAndLastTokenParsers(\"%{set-cookie}o\",\n                \"response.cookies\",   \"HTTP.SETCOOKIES\",\n                STRING_ONLY, FORMAT_STRING, 1));\n        parsers.addAll(createFirstAndLastTokenParsers(\"%{user-agent}i\",\n                \"request.user-agent\", \"HTTP.USERAGENT\",\n                STRING_ONLY, FORMAT_STRING, 1));\n        parsers.addAll(createFirstAndLastTokenParsers(\"%{referer}i\",\n                \"request.referer\",    \"HTTP.URI\",\n                STRING_ONLY, FORMAT_STRING, 1));\n\n        return parsers;\n    }\n\n    private void addExtraOutput(List<TokenParser> parsers,\n                                    final String nLogFormatToken,\n                                     TokenOutputField tokenOutputField) {\n        for (TokenParser tokenParser:parsers) {\n            if (tokenParser.getLogFormatToken().equals(nLogFormatToken)){\n                tokenParser.addOutputField(tokenOutputField);\n                return;\n            }\n        }\n    }\n\n    private List<TokenParser> createFirstAndLastTokenParsers(\n        final String nLogFormatToken,\n        final String nValueName,\n        final String nValueType,\n        final EnumSet<Casts> nCasts,\n        final String nRegex) {\n        return createFirstAndLastTokenParsers(nLogFormatToken, nValueName, nValueType, nCasts, nRegex, 0);\n    }\n\n    private List<TokenParser> createFirstAndLastTokenParsers(\n        final String nLogFormatToken,\n        final String nValueName,\n        final String nValueType,\n        final EnumSet<Casts> nCasts,\n        final String nRegex,\n        final int nPrio) {\n        List<TokenParser> parsers = new ArrayList<>(3);\n\n        // Quote from http://httpd.apache.org/docs/current/mod/mod_log_config.html#modifiers\n        //      The modifiers \"<\" and \">\" can be used for requests that have been internally redirected to\n        //      choose whether the original or final (respectively) request should be consulted.\n        //      By default, the % directives %s, %U, %T, %D, and %r look at the original request\n        //      while all others look at the final request.\n        //      So for example, %>s can be used to record the final status of the request and %<u can be used\n        //      to record the original authenticated user on a request that is internally redirected to an\n        //      unauthenticated resource.\n\n        switch(nLogFormatToken) {\n            case \"%s\":\n            case \"%U\":\n            case \"%T\":\n            case \"%{us}T\":\n            case \"%{ms}T\":\n            case \"%{s}T\":\n            case \"%D\":\n            case \"%r\":\n                // Quote: By default, the % directives %s, %U, %T, %D, and %r look at the original request\n                // So '%X' is the same value as '%<X' hence we output BOTH\n                parsers.add(new TokenParser(nLogFormatToken, nRegex, nPrio)\n                    .addOutputField(nValueType, nValueName,          nCasts)\n                    .addOutputField(nValueType, nValueName + \".original\", nCasts)\n                );\n\n                break;\n            default:\n                // Quote: By default, ... all others look at the final request.\n                // So '%X' is the same value as '%>X' hence we output BOTH\n                parsers.add(new TokenParser(nLogFormatToken, nRegex, nPrio)\n                    .addOutputField(nValueType, nValueName,           nCasts)\n                    .addOutputField(nValueType, nValueName + \".last\", nCasts)\n                );\n                break;\n        }\n\n        parsers.add(new TokenParser(nLogFormatToken.replaceFirst(\"%\", \"%<\"), nRegex, nPrio)\n            .addOutputField(nValueType, nValueName + \".original\", nCasts)\n        );\n\n        parsers.add(new TokenParser(nLogFormatToken.replaceFirst(\"%\", \"%>\"), nRegex, nPrio)\n            .addOutputField(nValueType, nValueName + \".last\", nCasts)\n        );\n\n        return parsers;\n    }\n\n\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/main/java/nl/basjes/parse/httpdlog/HttpdLogFormatDissector.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage nl.basjes.parse.httpdlog;\n\nimport nl.basjes.parse.core.Casts;\nimport nl.basjes.parse.core.Dissector;\nimport nl.basjes.parse.core.Parsable;\nimport nl.basjes.parse.core.Parser;\nimport nl.basjes.parse.core.exceptions.DissectionFailure;\nimport nl.basjes.parse.core.exceptions.InvalidDissectorException;\nimport nl.basjes.parse.httpdlog.dissectors.tokenformat.TokenFormatDissector;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.EnumSet;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport static nl.basjes.parse.core.Casts.NO_CASTS;\n\npublic class HttpdLogFormatDissector extends Dissector {\n\n    private static final Logger LOG = LoggerFactory.getLogger(HttpdLogFormatDissector.class);\n\n    // This value MUST be the same for all formats this dissector can wrap\n    public static final String INPUT_TYPE = \"HTTPLOGLINE\";\n\n    private final List<String> registeredLogFormats;\n    private final List<TokenFormatDissector> dissectors;\n    private TokenFormatDissector activeDissector;\n\n    public HttpdLogFormatDissector() {\n        registeredLogFormats = new ArrayList<>(16);\n        dissectors = new ArrayList<>(16);\n        activeDissector = null;\n    }\n\n    public HttpdLogFormatDissector(final String multiLineLogFormat) {\n        this();\n        addMultipleLogFormats(multiLineLogFormat);\n\n        if (enableJettyFix) {\n            addAdditionalLogFormatsToHandleJettyUseragentProblem();\n        }\n    }\n\n    // Jetty has two (historical) problems:\n    // 1) An empty user field was logged in the past as \" - \" instead of \"-\"\n    //    See: https://github.com/eclipse/jetty.project/commit/2332b4f\n    // 2) An empty useragent field was logged with an extra trailing space.\n    // This boolean enables a hack to parse these files.\n    private boolean enableJettyFix = false;\n\n    private void addAdditionalLogFormatsToHandleJettyUseragentProblem() {\n        for (String logFormat : getAllLogFormats()) {\n            if (logFormat.contains(\"\\\"%{User-Agent}i\\\"\")) {\n                LOG.info(\"Creating extra logformat to handle Jetty useragent problem.\");\n                String patchedLogFormat = logFormat.replace(\"\\\"%{User-Agent}i\\\"\", \"\\\"%{User-Agent}i\\\" \");\n                addLogFormat(patchedLogFormat);\n            }\n        }\n\n        // Jetty has suffered from this historical problem:\n        // An empty user field was logged in the past as \" - \" instead of \"-\"\n        // See: https://github.com/eclipse/jetty.project/commit/2332b4f\n        for (String logFormat : getAllLogFormats()) {\n            if (logFormat.contains(\"%u\")) {\n                LOG.info(\"Creating extra logformat to handle Jetty userfield problem.\");\n                String patchedLogFormat = logFormat.replace(\"%u\", \" %u \");\n                addLogFormat(patchedLogFormat);\n            }\n        }\n    }\n\n    public HttpdLogFormatDissector enableJettyFix() {\n        enableJettyFix = true;\n        return this;\n    }\n\n    public HttpdLogFormatDissector addMultipleLogFormats(final String multiLineLogFormat) {\n        return addLogFormat(Arrays.asList(multiLineLogFormat.split(\"\\\\r?\\\\n\")));\n    }\n\n    public HttpdLogFormatDissector addLogFormat(final List<String> logFormats) {\n        for (String logFormat : logFormats) {\n            addLogFormat(logFormat);\n        }\n        return this;\n    }\n\n    public HttpdLogFormatDissector addLogFormat(final String logFormat) {\n        if (logFormat == null || logFormat.trim().isEmpty()) {\n            return this; // Skip this one\n        }\n\n        if (logFormat.toUpperCase().trim().equals(\"ENABLE JETTY FIX\")) {\n            return enableJettyFix();\n        }\n\n        if (registeredLogFormats.contains(logFormat)) {\n            LOG.info(\"Skipping duplicate LogFormat: >>{}<<\", logFormat);\n            return this; // We already have this one\n        }\n\n        registeredLogFormats.add(logFormat);\n\n        switch (determineMostLikelyLogFormat(logFormat)) {\n            case APACHE:\n                LOG.info(\"Registering APACHE HTTPD LogFormat[{}]= >>{}<<\", dissectors.size(), logFormat);\n                dissectors.add(new ApacheHttpdLogFormatDissector(logFormat));\n                break;\n            case NGINX:\n                LOG.info(\"Registering NGINX LogFormat[{}]= >>{}<<\", dissectors.size(), logFormat);\n                dissectors.add(new NginxHttpdLogFormatDissector(logFormat));\n                break;\n            default:\n                LOG.error(\"Unable to determine if this is an APACHE or a NGINX LogFormat= >>{}<<\", logFormat);\n                break;\n        }\n        return this;\n    }\n\n    private enum LogFormatType {\n        APACHE,\n        NGINX,\n        UNKNOWN\n    }\n\n    private LogFormatType determineMostLikelyLogFormat(final String logFormat) {\n        if (ApacheHttpdLogFormatDissector.looksLikeApacheFormat(logFormat)) {\n            return LogFormatType.APACHE;\n        }\n        if (NginxHttpdLogFormatDissector.looksLikeNginxFormat(logFormat)) {\n            return LogFormatType.NGINX;\n        }\n        // We do not know\n        return LogFormatType.UNKNOWN;\n    }\n\n\n    @Override\n    public boolean initializeFromSettingsParameter(String multiLineLogFormat) {\n        addMultipleLogFormats(multiLineLogFormat);\n        return true;\n    }\n\n    @Override\n    public <RECORD> void createAdditionalDissectors(Parser<RECORD> parser) {\n        for (Dissector dissector : dissectors) {\n            dissector.createAdditionalDissectors(parser);\n        }\n    }\n\n    @Override\n    public void dissect(Parsable<?> parsable, String inputname) throws DissectionFailure {\n        if (dissectors.isEmpty()) {\n            throw new DissectionFailure(\"We need one or more logformats before we can dissect.\");\n        }\n\n        // Initial: We must determine the right dissector\n        if (activeDissector == null) {\n            activeDissector = dissectors.get(0);\n            LOG.info(\"At start we use LogFormat[0]= >>{}<<\", activeDissector.getLogFormat());\n        }\n\n        try {\n            activeDissector.dissect(parsable, inputname);\n        } catch (DissectionFailure df) {\n            if (dissectors.size() > 1) {\n                int index = 0;\n                for (TokenFormatDissector dissector : dissectors) {\n                    try {\n                        dissector.dissect(parsable, inputname);\n                        LOG.info(\"Switched to LogFormat[{}]= >>{}<<\", index, activeDissector.getLogFormat());\n                        activeDissector = dissector;\n                        return;\n                    } catch (DissectionFailure e) {\n                        index++;\n                        // We ignore the error and try the next one.\n                    }\n                }\n            }\n            throw df;\n        }\n    }\n\n    @Override\n    public String getInputType() {\n        return INPUT_TYPE;\n    }\n\n    @Override\n    public List<String> getPossibleOutput() {\n        if (dissectors.isEmpty()) {\n            return Collections.emptyList();\n        }\n\n        Set<String> result = new HashSet<>(32); // Go via a Set to deduplicate the fields\n        for (Dissector dissector : dissectors) {\n            result.addAll(dissector.getPossibleOutput());\n        }\n\n        return new ArrayList<>(result);\n    }\n\n    @Override\n    public EnumSet<Casts> prepareForDissect(String inputname, String outputname) {\n        if (dissectors.isEmpty()) {\n            return NO_CASTS;\n        }\n\n        EnumSet<Casts> result = EnumSet.noneOf(Casts.class); // Start empty\n        for (Dissector dissector : dissectors) {\n            result.addAll(dissector.prepareForDissect(inputname, outputname));\n        }\n        return result;\n    }\n\n    @Override\n    public void prepareForRun() throws InvalidDissectorException {\n        if (dissectors.isEmpty()) {\n            throw new InvalidDissectorException(\"Cannot run without logformats\");\n        }\n\n        for (Dissector dissector : dissectors) {\n            if (!INPUT_TYPE.equals(dissector.getInputType())) {\n                throw new InvalidDissectorException(\"All dissectors controlled by \" + this.getClass().getCanonicalName()\n                    + \" MUST have \\\"\" + INPUT_TYPE + \"\\\" as their inputtype.\");\n            }\n            dissector.prepareForRun();\n        }\n    }\n\n\n    private List<String> getAllLogFormats() {\n        List<String> result = new ArrayList<>(dissectors.size());\n        for (Dissector dissector : dissectors) {\n            if (dissector instanceof TokenFormatDissector) {\n                result.add(((TokenFormatDissector) dissector).getLogFormat());\n            }\n        }\n        return result;\n    }\n\n    @Override\n    protected void initializeNewInstance(Dissector newInstance) {\n        if (dissectors.isEmpty()) {\n            return;\n        }\n\n        if (newInstance instanceof HttpdLogFormatDissector) {\n            ((HttpdLogFormatDissector) newInstance).addLogFormat(getAllLogFormats());\n\n            if (enableJettyFix) {\n                ((HttpdLogFormatDissector) newInstance).enableJettyFix();\n            }\n\n        } else {\n            LOG.error(\"============================== WTF == {}\", newInstance.getClass().getCanonicalName());\n        }\n\n    }\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/main/java/nl/basjes/parse/httpdlog/HttpdLoglineParser.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.httpdlog;\n\nimport nl.basjes.parse.core.Parser;\nimport nl.basjes.parse.httpdlog.dissectors.HttpFirstLineDissector;\nimport nl.basjes.parse.httpdlog.dissectors.HttpFirstLineProtocolDissector;\nimport nl.basjes.parse.httpdlog.dissectors.HttpUriDissector;\nimport nl.basjes.parse.httpdlog.dissectors.ModUniqueIdDissector;\nimport nl.basjes.parse.httpdlog.dissectors.QueryStringFieldDissector;\nimport nl.basjes.parse.httpdlog.dissectors.RequestCookieListDissector;\nimport nl.basjes.parse.httpdlog.dissectors.ResponseSetCookieDissector;\nimport nl.basjes.parse.httpdlog.dissectors.ResponseSetCookieListDissector;\nimport nl.basjes.parse.httpdlog.dissectors.TimeStampDissector;\nimport nl.basjes.parse.httpdlog.dissectors.translate.ConvertCLFIntoNumber;\nimport nl.basjes.parse.httpdlog.dissectors.translate.ConvertNumberIntoCLF;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class HttpdLoglineParser<RECORD> extends Parser<RECORD> {\n\n    private static final Logger LOG = LoggerFactory.getLogger(HttpdLoglineParser.class);\n\n    // --------------------------------------------\n\n    public HttpdLoglineParser(\n            final Class<RECORD> clazz,\n            final String logformat) {\n        super(clazz);\n        logVersion();\n        setupDissectors(logformat, null);\n    }\n\n    // --------------------------------------------\n\n    public static void logVersion(){\n        String[] lines = {\n            \"Apache HTTPD & NGINX Access log parsing made easy\",\n            \"For more information: https://github.com/nielsbasjes/logparser\",\n            \"Copyright (C) 2011-2023 Niels Basjes - License Apache 2.0\"\n        };\n        String version = getVersion();\n        int width = version.length();\n        for (String line: lines) {\n            width = Math.max(width, line.length());\n        }\n\n        LOG.info(\"\");\n        LOG.info(\"/-{}-\\\\\", padding('-', width));\n        logLine(version, width);\n        LOG.info(\"+-{}-+\", padding('-', width));\n        for (String line: lines) {\n            logLine(line, width);\n        }\n        LOG.info(\"\\\\-{}-/\", padding('-', width));\n        LOG.info(\"\");\n    }\n\n    private static String padding(char letter, int count) {\n        StringBuilder sb = new StringBuilder(128);\n        for (int i=0; i <count; i++) {\n            sb.append(letter);\n        }\n        return sb.toString();\n    }\n\n    private static void logLine(String line, int width) {\n        LOG.info(\"| {}{} |\", line, padding(' ', width - line.length()));\n    }\n\n    // --------------------------------------------\n\n    public static String getVersion() {\n        return \"LogParser \" + Version.getProjectVersion() +\n            \" (\" + Version.getGitCommitIdDescribeShort() + \" @ \" + Version.getBuildTimestamp() + \")\";\n    }\n\n    public HttpdLoglineParser(\n            final Class<RECORD> clazz,\n            final String logformat,\n            final String timestampFormat) {\n        super(clazz);\n        setupDissectors(logformat, timestampFormat);\n    }\n\n    private void setupDissectors(\n            final String logformat,\n            final String timestampFormat) {\n        // The pieces we have to get there\n        addDissector(new HttpdLogFormatDissector(logformat));\n        addDissector(new TimeStampDissector(\"TIME.STAMP\", timestampFormat));\n        addDissector(new TimeStampDissector(\"TIME.ISO8601\", \"yyyy-MM-dd'T'HH:mm:ssXXX\"));\n        addDissector(new HttpFirstLineDissector());\n        addDissector(new HttpFirstLineProtocolDissector());\n        addDissector(new HttpUriDissector());\n        addDissector(new QueryStringFieldDissector());\n        addDissector(new RequestCookieListDissector());\n        addDissector(new ResponseSetCookieListDissector());\n        addDissector(new ResponseSetCookieDissector());\n        addDissector(new ModUniqueIdDissector());\n\n        // Type translators\n        addDissector(new ConvertCLFIntoNumber(\"BYTESCLF\", \"BYTES\"));\n        addDissector(new ConvertNumberIntoCLF(\"BYTES\", \"BYTESCLF\"));\n\n        // And we define the input for this parser\n        setRootType(HttpdLogFormatDissector.INPUT_TYPE);\n    }\n\n    // --------------------------------------------\n\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/main/java/nl/basjes/parse/httpdlog/NginxHttpdLogFormatDissector.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.httpdlog;\n\nimport nl.basjes.parse.core.Casts;\nimport nl.basjes.parse.core.Parsable;\nimport nl.basjes.parse.core.Parser;\nimport nl.basjes.parse.core.SimpleDissector;\nimport nl.basjes.parse.core.Value;\nimport nl.basjes.parse.core.exceptions.DissectionFailure;\nimport nl.basjes.parse.httpdlog.dissectors.nginxmodules.CoreLogModule;\nimport nl.basjes.parse.httpdlog.dissectors.nginxmodules.GeoIPModule;\nimport nl.basjes.parse.httpdlog.dissectors.nginxmodules.KubernetesIngressModule;\nimport nl.basjes.parse.httpdlog.dissectors.nginxmodules.NginxModule;\nimport nl.basjes.parse.httpdlog.dissectors.nginxmodules.SslModule;\nimport nl.basjes.parse.httpdlog.dissectors.nginxmodules.UpstreamModule;\nimport nl.basjes.parse.httpdlog.dissectors.nginxmodules.VariousModule;\nimport nl.basjes.parse.httpdlog.dissectors.tokenformat.TokenFormatDissector;\nimport nl.basjes.parse.httpdlog.dissectors.tokenformat.TokenParser;\nimport nl.basjes.parse.httpdlog.dissectors.translate.ConvertMillisecondsIntoMicroseconds;\nimport nl.basjes.parse.httpdlog.dissectors.translate.ConvertSecondsWithMillisStringDissector;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.util.ArrayList;\nimport java.util.EnumSet;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport static nl.basjes.parse.core.Casts.STRING_OR_LONG;\n\n@SuppressWarnings({\n    \"PMD.LongVariable\", // I like my variable names this way\n    \"PMD.CyclomaticComplexity\", \"PMD.OnlyOneReturn\",\n    \"PMD.BeanMembersShouldSerialize\", // No beans here\n    \"PMD.DataflowAnomalyAnalysis\" // Results in a lot of mostly useless messages.\n})\npublic class NginxHttpdLogFormatDissector extends TokenFormatDissector {\n\n    private static final Logger LOG = LoggerFactory.getLogger(NginxHttpdLogFormatDissector.class);\n\n    public NginxHttpdLogFormatDissector(final String logFormat) {\n        super(logFormat);\n        setInputType(HttpdLogFormatDissector.INPUT_TYPE);\n    }\n\n    public NginxHttpdLogFormatDissector() {\n        super();\n        setInputType(HttpdLogFormatDissector.INPUT_TYPE);\n    }\n\n    private void overrideLogFormat(String originalLogformat, String logformat) {\n        LOG.debug(\"Specified logformat \\\"{}\\\" was mapped to {}\", originalLogformat, logformat);\n        super.setLogFormat(logformat);\n    }\n\n    @Override\n    public void setLogFormat(final String logformat) {\n        // https://nginx.org/en/docs/http/ngx_http_log_module.html#log_format\n        // The configuration always includes the predefined “combined” format:\n\n        //  log_format combined '$remote_addr - $remote_user [$time_local] '\n        //                      '\"$request\" $status $body_bytes_sent '\n        //                      '\"$http_referer\" \"$http_user_agent\"';\n        switch (logformat.toLowerCase(Locale.getDefault())) {\n            case \"combined\":\n                overrideLogFormat(logformat,\n                    \"$remote_addr - $remote_user [$time_local] \\\"$request\\\" $status $body_bytes_sent \\\"$http_referer\\\" \\\"$http_user_agent\\\"\");\n                break;\n            default:\n                super.setLogFormat(logformat);\n                break;\n        }\n    }\n\n    public static boolean looksLikeNginxFormat(String logFormat) {\n        if (logFormat.indexOf('$') != -1) {\n            return true;\n        }\n        switch (logFormat.toLowerCase(Locale.getDefault())) {\n            case \"combined\":\n                return true;\n            default:\n                return false;\n        }\n    }\n\n    // --------------------------------------------\n\n    @Override\n    public String decodeExtractedValue(String tokenName, String value) {\n        if (value == null || value.equals(\"\")) {\n            return value;\n        }\n\n        // In Apache logfiles a '-' means a 'not specified' / 'empty' value.\n        if (value.equals(\"-\")) {\n            return null;\n        }\n\n        return value;\n    }\n\n    private static final List<NginxModule> MODULES = new ArrayList<>();\n    static {\n        MODULES.add(new CoreLogModule());\n        MODULES.add(new UpstreamModule());\n        MODULES.add(new SslModule());\n        MODULES.add(new GeoIPModule());\n        MODULES.add(new VariousModule());\n        MODULES.add(new KubernetesIngressModule());\n    }\n\n    // --------------------------------------------\n    @Override\n    protected List<TokenParser> createAllTokenParsers() {\n        List<TokenParser> parsers = new ArrayList<>();\n        MODULES.forEach(m -> parsers.addAll(m.getTokenParsers()));\n\n        return parsers;\n    }\n\n    @Override\n    public <RECORD> void createAdditionalDissectors(Parser<RECORD> parser) {\n        super.createAdditionalDissectors(parser);\n        parser.addDissector(new BinaryIPDissector());\n        parser.addDissector(new ConvertSecondsWithMillisStringDissector(\"SECOND_MILLIS\",            \"MILLISECONDS\"));\n        parser.addDissector(new ConvertSecondsWithMillisStringDissector(\"TIME.EPOCH_SECOND_MILLIS\", \"TIME.EPOCH\"));\n        parser.addDissector(new ConvertMillisecondsIntoMicroseconds(\"MILLISECONDS\", \"MICROSECONDS\"));\n\n        MODULES.forEach(m -> parser.addDissectors(m.getDissectors()));\n    }\n\n    public static class BinaryIPDissector extends SimpleDissector {\n\n        private static final HashMap<String, EnumSet<Casts>> EPOCH_MILLIS_CONFIG = new HashMap<>();\n        static {\n            EPOCH_MILLIS_CONFIG.put(\"IP:\", STRING_OR_LONG);\n        }\n        public BinaryIPDissector() {\n            super(\"IP_BINARY\", EPOCH_MILLIS_CONFIG);\n        }\n\n        private static final String CAPTURE_HEX_BYTE = \"\\\\\\\\x([0-9a-fA-F][0-9a-fA-F])\";\n        final Pattern binaryIPPattern = Pattern.compile(\n            CAPTURE_HEX_BYTE+CAPTURE_HEX_BYTE+CAPTURE_HEX_BYTE+CAPTURE_HEX_BYTE\n        );\n\n        @Override\n        public void dissect(Parsable<?> parsable, String inputname, Value value) throws DissectionFailure {\n            Matcher matcher = binaryIPPattern.matcher(value.getString());\n            if (matcher.matches()) {\n                String ip =\n                    String.valueOf(Utils.hexCharsToByte(matcher.group(1))) + '.' +\n                    String.valueOf(Utils.hexCharsToByte(matcher.group(2))) + '.' +\n                    String.valueOf(Utils.hexCharsToByte(matcher.group(3))) + '.' +\n                    String.valueOf(Utils.hexCharsToByte(matcher.group(4)));\n                parsable.addDissection(inputname, \"IP\", \"\", ip);\n            }\n        }\n    }\n\n    // This is marked as deprecated because we want to mark all uses of this as \"undesirable\"\n    @Deprecated\n    public static class NotYetImplemented extends NotImplementedTokenParser {\n        private static final String FIELD_PREFIX = \"nginx_parameter\";\n        public NotYetImplemented(final String nLogFormatToken) {\n            super(nLogFormatToken, FIELD_PREFIX, 0);\n        }\n\n        public NotYetImplemented(final String nLogFormatToken, final String regex) {\n            super(nLogFormatToken, FIELD_PREFIX, regex, 0);\n        }\n\n        public NotYetImplemented(final String nLogFormatToken, final String regex, final int prio) {\n            super(nLogFormatToken, FIELD_PREFIX, regex, prio);\n        }\n\n        public NotYetImplemented(final String nLogFormatToken, final int prio) {\n            super(nLogFormatToken, FIELD_PREFIX, \"[^\\\" ]*\", prio);\n        }\n    }\n\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/main/java/nl/basjes/parse/httpdlog/Utils.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.httpdlog;\n\nimport org.apache.commons.text.StringEscapeUtils;\n\nimport java.io.UnsupportedEncodingException;\nimport java.net.URLDecoder;\nimport java.net.URLEncoder;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.regex.Pattern;\n\npublic final class Utils {\n\n    private Utils() {}\n\n    private static final Pattern CHOPPED_STANDARD       = Pattern.compile(\"%[0-9A-Fa-f]?$\");\n    private static final Pattern VALID_NON_STANDARD     = Pattern.compile(\"%u([0-9A-Fa-f][0-9A-Fa-f])+\");\n    private static final Pattern CHOPPED_NON_STANDARD   = Pattern.compile(\"%u[0-9A-Fa-f]{0,3}$\");\n\n    /**\n     * The main goal of the resilientUrlDecode is to have a UrlDecode that keeps working\n     * even if the input is seriously flawed or even uses a rejected standard.\n     * @param input the UrlEncoded input string\n     * @return Url decoded result string\n     */\n    public static String resilientUrlDecode(String input) {\n        String cookedInput = input;\n\n        if (cookedInput.indexOf('%') > -1) {\n            // Discard chopped encoded char at the end of the line (there is no way to know what it was)\n            cookedInput = CHOPPED_STANDARD.matcher(cookedInput).replaceAll(\"\");\n\n            // Handle non standard (rejected by W3C) encoding that is used anyway by some\n            // See: https://stackoverflow.com/a/5408655/114196\n            if (cookedInput.contains(\"%u\")) {\n                cookedInput = replaceString(cookedInput, \"%u00\", \"%u\");\n\n                // Transform all existing non standard into UTF-8 standard.\n                cookedInput = VALID_NON_STANDARD.matcher(cookedInput).replaceAll(\"%$1\");\n\n                // Discard chopped encoded char at the end of the line\n                cookedInput = CHOPPED_NON_STANDARD.matcher(cookedInput).replaceAll(\"\");\n            }\n        }\n\n        try {\n            return URLDecoder.decode(cookedInput, \"UTF-8\");\n        } catch (UnsupportedEncodingException e) {\n            // Will never happen because the encoding is hardcoded\n            return null;\n        }\n    }\n\n\n    public static byte hexCharsToByte(String twoHexDigits) {\n        if (twoHexDigits == null || twoHexDigits.length() != 2) {\n            throw new IllegalArgumentException(\"URLDecoder: Illegal hex characters : \\\"\" + twoHexDigits + \"\\\"\");\n        }\n        return hexCharsToByte(twoHexDigits.charAt(0), twoHexDigits.charAt(1));\n    }\n\n    public static byte hexCharsToByte(char c1, char c2){\n        byte result;\n        switch (c1) {\n            case '0': result = (byte)0x00; break;\n            case '1': result = (byte)0x10; break;\n            case '2': result = (byte)0x20; break;\n            case '3': result = (byte)0x30; break;\n            case '4': result = (byte)0x40; break;\n            case '5': result = (byte)0x50; break;\n            case '6': result = (byte)0x60; break;\n            case '7': result = (byte)0x70; break;\n            case '8': result = (byte)0x80; break;\n            case '9': result = (byte)0x90; break;\n            case 'a': result = (byte)0xa0; break;\n            case 'b': result = (byte)0xb0; break;\n            case 'c': result = (byte)0xc0; break;\n            case 'd': result = (byte)0xd0; break;\n            case 'e': result = (byte)0xe0; break;\n            case 'f': result = (byte)0xf0; break;\n            case 'A': result = (byte)0xa0; break;\n            case 'B': result = (byte)0xb0; break;\n            case 'C': result = (byte)0xc0; break;\n            case 'D': result = (byte)0xd0; break;\n            case 'E': result = (byte)0xe0; break;\n            case 'F': result = (byte)0xf0; break;\n            default: throw new IllegalArgumentException(\"URLDecoder: Illegal hex characters (char 1): '\" + c1 + \"'\");\n        }\n        switch (c2) {\n            case '0': break; // result |= (byte)0x00; --> An OR with only 0 bits is useless\n            case '1': result |= (byte)0x01; break;\n            case '2': result |= (byte)0x02; break;\n            case '3': result |= (byte)0x03; break;\n            case '4': result |= (byte)0x04; break;\n            case '5': result |= (byte)0x05; break;\n            case '6': result |= (byte)0x06; break;\n            case '7': result |= (byte)0x07; break;\n            case '8': result |= (byte)0x08; break;\n            case '9': result |= (byte)0x09; break;\n            case 'a': result |= (byte)0x0a; break;\n            case 'b': result |= (byte)0x0b; break;\n            case 'c': result |= (byte)0x0c; break;\n            case 'd': result |= (byte)0x0d; break;\n            case 'e': result |= (byte)0x0e; break;\n            case 'f': result |= (byte)0x0f; break;\n            case 'A': result |= (byte)0x0a; break;\n            case 'B': result |= (byte)0x0b; break;\n            case 'C': result |= (byte)0x0c; break;\n            case 'D': result |= (byte)0x0d; break;\n            case 'E': result |= (byte)0x0e; break;\n            case 'F': result |= (byte)0x0f; break;\n            default: throw new IllegalArgumentException(\"URLDecoder: Illegal hex characters (char 2): '\" + c2 + \"'\");\n        }\n\n        return result;\n    }\n\n    /**\n     * Decoder for this specification:\n     *\n     * http://httpd.apache.org/docs/current/mod/mod_log_config.html#formats\n     * Format Notes\n     * For security reasons, starting with version 2.0.46, non-printable and other special characters\n     * in %r, %i and %o are escaped using \\xhh sequences, where hh stands for the hexadecimal representation of\n     * the raw byte. Exceptions from this rule are \" and \\, which are escaped by prepending a backslash, and\n     * all whitespace characters, which are written in their C-style notation (\\n, \\t, etc).\n     *\n     * Actual method in the original code:  ap_escape_logitem\n     * https://github.com/apache/httpd/blob/trunk/server/util.c#L2001\n     *\n     * @param input The value as it was logged by the Apache HTTPD.\n     * @return      The 'decoded' version of the logged value.\n     */\n    public static String decodeApacheHTTPDLogValue(String input){\n        if (input == null || input.length() == 0) {\n            return input;\n        }\n\n        if (!input.contains(\"\\\\\")) {\n            return input;\n        }\n\n        StringBuilder sb = new StringBuilder(input.length());\n\n        // https://stackoverflow.com/q/8894258/114196 : Fastest way to iterate over all the chars in a String\n        for (int i = 0; i < input.length(); i++) {\n            char chr = input.charAt(i);\n\n            if (chr == '\\\\') {\n                chr = input.charAt(++i);\n                switch (chr){\n                    case '\"':\n                    case '\\\\':\n                        sb.append(chr);\n                        break;\n                    case 'b':\n                        sb.append('\\b');\n                        break;\n                    case 'n':\n                        sb.append('\\n');\n                        break;\n                    case 'r':\n                        sb.append('\\r');\n                        break;\n                    case 't':\n                        sb.append('\\t');\n                        break;\n                    case 'v':\n                        sb.append((char)hexCharsToByte('0', 'b'));\n                        break;\n                    case 'x':\n                        // This should be \\xhh  (hh = [0-9a-f][0-9a-f])\n                        char chr1 = input.charAt(++i);\n                        char chr2 = input.charAt(++i);\n                        sb.append((char)hexCharsToByte(chr1, chr2));\n                        break;\n                    default:\n                        // This shouldn't happen.\n                        // Let's just append the unmodified input for now.\n                        sb.append('\\\\').append(chr);\n                }\n            } else {\n                sb.append(chr);\n            }\n\n        }\n        return sb.toString();\n    }\n\n    private static final Map<String, String> HTML_ENTITY_REPLACE_MAP;\n\n    private static String htmlEntityToURLEncoded(String entity) {\n        try {\n            return URLEncoder.encode(StringEscapeUtils.unescapeHtml4(entity), StandardCharsets.UTF_8.toString());\n        } catch (UnsupportedEncodingException e) {\n            // This will never happen\n        }\n        return null;\n    }\n\n    static {\n        // The list of common entities I chose to support on their name.\n        List<String> entities = Arrays.asList(\n            \"&nbsp;\",\n            \"&lt;\",\n            \"&gt;\",\n            \"&amp;\",\n            \"&quot;\",\n            \"&tilde;\",\n            \"&cent;\",\n            \"&pound;\",\n            \"&yen;\",\n            \"&euro;\",\n            \"&copy;\",\n            \"&reg;\"\n        );\n\n        Map<String, String> aMap = new HashMap<>();\n        entities.forEach(entity -> aMap.put(entity, htmlEntityToURLEncoded(entity)));\n        HTML_ENTITY_REPLACE_MAP = Collections.unmodifiableMap(aMap);\n    }\n\n    /**\n     * Sometimes people put HTML encoded chars into a URL.\n     * Because it is very hard to correctly decode these we are just making them 'inert'\n     * so they do not break the rest of the processing.\n     * @param uriString The input URI\n     * @return Cleaned string\n     */\n    public static String makeHTMLEncodedInert(String uriString) {\n        String result = uriString;\n\n        // For some entities we have a valid replace value.\n        for (Map.Entry<String, String> entry: HTML_ENTITY_REPLACE_MAP.entrySet()) {\n            result = replaceString(result, entry.getKey(), entry.getValue());\n        }\n\n        // For the rest we simply make it inert\n        // TODO: Convert the codepoint to valid UTF-8\n        // Named entities ( like &gt; and &euro; )\n        result = result.replaceAll(\"&([a-zA-Z]+;)\", \"*$1\");\n\n        // Numerical decimal entities\n        result = result.replaceAll(\"&(#[0-9a-fA-F]+;)\", \"*$1\");\n\n        // Numerical hexadecimal entities\n        result = result.replaceAll(\"&(#x[0-9a-fA-F]+;)\", \"*$1\");\n\n        return result;\n    }\n\n    // Copied from Yauaa\n    public static String replaceString(\n        final String input,\n        final String searchFor,\n        final String replaceWith\n    ){\n        //startIdx and idxSearchFor delimit various chunks of input; these\n        //chunks always end where searchFor begins\n        int startIdx = 0;\n        int idxSearchFor = input.indexOf(searchFor, startIdx);\n        if (idxSearchFor < 0) {\n            return input;\n        }\n        final StringBuilder result = new StringBuilder(input.length()+32);\n\n        while (idxSearchFor >= 0) {\n            //grab a part of input which does not include searchFor\n            result.append(input, startIdx, idxSearchFor);\n            //add replaceWith to take place of searchFor\n            result.append(replaceWith);\n\n            //reset the startIdx to just after the current match, to see\n            //if there are any further matches\n            startIdx = idxSearchFor + searchFor.length();\n            idxSearchFor = input.indexOf(searchFor, startIdx);\n        }\n        //the final chunk will go to the end of input\n        result.append(input.substring(startIdx));\n        return result.toString();\n    }\n\n\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/main/java/nl/basjes/parse/httpdlog/dissectors/HttpFirstLineDissector.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.httpdlog.dissectors;\n\nimport nl.basjes.parse.core.Casts;\nimport nl.basjes.parse.core.Dissector;\nimport nl.basjes.parse.core.Parsable;\nimport nl.basjes.parse.core.ParsedField;\nimport nl.basjes.parse.core.exceptions.DissectionFailure;\n\nimport java.util.ArrayList;\nimport java.util.EnumSet;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport static nl.basjes.parse.core.Casts.STRING_ONLY;\n\npublic class HttpFirstLineDissector extends Dissector {\n    // --------------------------------------------\n    // The \"first line\" of a request can be split up a bit further\n    // See for more details: http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html\n    // In https://tools.ietf.org/html/rfc7230#section-3.1.1 it says:\n    //      Recipients typically parse the request-line into its component parts\n    //      by splitting on whitespace (see Section 3.5), since no whitespace is\n    //      allowed in the three components.  Unfortunately, some user agents\n    //      fail to properly encode or exclude whitespace found in hypertext\n    //      references, resulting in those disallowed characters being sent in a\n    //      request-target.\n\n    // So this means:\n    // - Method = Single Word\n    // - Request URI = String that can contain ANY letters\n    // - HTTP version = HTTP/[0-9]+\\.[0-9]+\n    // The HTTP version has been made optional to allow parsing the log lines you get when the URI is > 8KB\n    // In that scenario the HTTP/x.x part will not be logged at all.\n    // In case of using mod_reqtimeout simply opening a connection and wait for the timeout without entering any data\n    // results in an empty firstline. I.e. a \"-\"\n    // The actual regex has been reduced to '.*' because we want to continue even if someone throws in complete garbage.\n    public static final String FIRSTLINE_REGEX =\n            \".*\";\n\n    private final Pattern firstlineSplitter = Pattern\n            .compile(\"^([a-zA-Z-_]+) (.*) (HTTP/[0-9]+\\\\.[0-9]+)$\");\n\n    private final Pattern tooLongFirstlineSplitter = Pattern\n            .compile(\"^([a-zA-Z-_]+) (.*)$\");\n\n    // --------------------------------------------\n\n    private static final String HTTP_FIRSTLINE = \"HTTP.FIRSTLINE\";\n    @Override\n    public String getInputType() {\n        return HTTP_FIRSTLINE;\n    }\n\n    // --------------------------------------------\n\n    @Override\n    public List<String> getPossibleOutput() {\n        List<String> result = new ArrayList<>();\n        result.add(\"HTTP.METHOD:method\");\n        result.add(\"HTTP.URI:uri\");\n        result.add(\"HTTP.PROTOCOL_VERSION:protocol\");\n        return result;\n    }\n\n    // --------------------------------------------\n\n    @Override\n    public void dissect(final Parsable<?> parsable, final String inputname) throws DissectionFailure {\n        final ParsedField field = parsable.getParsableField(HTTP_FIRSTLINE, inputname);\n\n        final String fieldValue = field.getValue().getString();\n        if (fieldValue == null || fieldValue.isEmpty() || \"-\".equals(fieldValue)){\n            return; // Nothing to do here\n        }\n\n        // Now we create a matcher for this line\n        Matcher matcher = firstlineSplitter.matcher(fieldValue);\n\n        // Is it all as expected?\n        boolean matches = matcher.find();\n\n        if (matches && matcher.groupCount() == 3) {\n            outputDissection(parsable, inputname, \"HTTP.METHOD\", \"method\", matcher, 1);\n            outputDissection(parsable, inputname, \"HTTP.URI\", \"uri\", matcher, 2);\n            outputDissection(parsable, inputname, \"HTTP.PROTOCOL_VERSION\", \"protocol\", matcher, 3);\n            return;\n        }\n\n        // In the scenario that the actual URI is too long the last part (\"HTTP/1.1\") may have been cut off by the\n        // Apache HTTPD webserver. To still be able to parse these we try that pattern too\n\n        // Now we create a matcher for this line\n        matcher = tooLongFirstlineSplitter.matcher(fieldValue);\n\n        // Is it all as expected?\n        matches = matcher.find();\n\n        if (matches && matcher.groupCount() == 2) {\n            outputDissection(parsable, inputname, \"HTTP.METHOD\", \"method\", matcher, 1);\n            outputDissection(parsable, inputname, \"HTTP.URI\", \"uri\", matcher, 2);\n            parsable.addDissection(inputname, \"HTTP.PROTOCOL_VERSION\", \"protocol\", (String) null);\n        }\n    }\n\n    private void outputDissection(Parsable<?> parsable,\n                                  String inputname,\n                                  String type,\n                                  String name,\n                                  Matcher matcher,\n                                  int offset)\n            throws DissectionFailure {\n        if (requestedParameters.contains(name)) {\n            parsable.addDissection(inputname, type, name, matcher.group(offset));\n        }\n    }\n\n    // --------------------------------------------\n\n    private final Set<String> requestedParameters = new HashSet<>(16);\n\n    @Override\n    public EnumSet<Casts> prepareForDissect(final String inputname, final String outputname) {\n        requestedParameters.add(extractFieldName(inputname, outputname));\n        return STRING_ONLY;\n    }\n\n    // --------------------------------------------\n\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/main/java/nl/basjes/parse/httpdlog/dissectors/HttpFirstLineProtocolDissector.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.httpdlog.dissectors;\n\nimport nl.basjes.parse.core.Casts;\nimport nl.basjes.parse.core.Dissector;\nimport nl.basjes.parse.core.Parsable;\nimport nl.basjes.parse.core.ParsedField;\nimport nl.basjes.parse.core.exceptions.DissectionFailure;\n\nimport java.util.ArrayList;\nimport java.util.EnumSet;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport static nl.basjes.parse.core.Casts.STRING_ONLY;\n\npublic class HttpFirstLineProtocolDissector extends Dissector {\n\n    // --------------------------------------------\n\n    private static final String INPUT_TYPE = \"HTTP.PROTOCOL_VERSION\";\n    @Override\n    public String getInputType() {\n        return INPUT_TYPE;\n    }\n\n    // --------------------------------------------\n\n    @Override\n    public List<String> getPossibleOutput() {\n        List<String> result = new ArrayList<>();\n        result.add(\"HTTP.PROTOCOL:\");\n        result.add(\"HTTP.PROTOCOL.VERSION:version\");\n        return result;\n    }\n\n    // --------------------------------------------\n\n    @Override\n    public void dissect(final Parsable<?> parsable, final String inputname) throws DissectionFailure {\n        final ParsedField field = parsable.getParsableField(INPUT_TYPE, inputname);\n\n        final String fieldValue = field.getValue().getString();\n        if (fieldValue == null || fieldValue.isEmpty() || \"-\".equals(fieldValue)){\n            return; // Nothing to do here\n        }\n\n        String[] protocol = fieldValue.split(\"/\", 2);\n\n        if (protocol.length == 2) {\n            outputDissection(parsable, inputname, \"HTTP.PROTOCOL\", \"\", protocol[0]);\n            outputDissection(parsable, inputname, \"HTTP.PROTOCOL.VERSION\", \"version\", protocol[1]);\n            return;\n        }\n\n        // In the scenario that the actual URI is too long the last part (\"HTTP/1.1\") may have been cut off by the\n        // Apache HTTPD webserver. To still be able to parse these we try that pattern too\n\n        parsable.addDissection(inputname, \"HTTP.PROTOCOL\", \"\", (String) null);\n        parsable.addDissection(inputname, \"HTTP.PROTOCOL.VERSION\", \"version\", (String) null);\n    }\n\n    private void outputDissection(Parsable<?> parsable,\n                                  String inputname,\n                                  String type,\n                                  String name,\n                                  String value)\n            throws DissectionFailure {\n        if (requestedParameters.contains(name)) {\n            parsable.addDissection(inputname, type, name, value);\n        }\n    }\n\n    // --------------------------------------------\n\n    private final Set<String> requestedParameters = new HashSet<>(16);\n\n    @Override\n    public EnumSet<Casts> prepareForDissect(final String inputname, final String outputname) {\n        requestedParameters.add(extractFieldName(inputname, outputname));\n        return STRING_ONLY;\n    }\n\n    // --------------------------------------------\n\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/main/java/nl/basjes/parse/httpdlog/dissectors/HttpUriDissector.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.httpdlog.dissectors;\n\nimport nl.basjes.parse.core.Casts;\nimport nl.basjes.parse.core.Dissector;\nimport nl.basjes.parse.core.Parsable;\nimport nl.basjes.parse.core.ParsedField;\nimport nl.basjes.parse.core.exceptions.DissectionFailure;\nimport org.apache.commons.codec.net.URLCodec;\nimport org.apache.commons.text.StringEscapeUtils;\n\nimport java.net.URI;\nimport java.util.ArrayList;\nimport java.util.BitSet;\nimport java.util.EnumSet;\nimport java.util.List;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport static java.nio.charset.StandardCharsets.US_ASCII;\nimport static java.nio.charset.StandardCharsets.UTF_8;\nimport static nl.basjes.parse.core.Casts.NO_CASTS;\nimport static nl.basjes.parse.core.Casts.STRING_ONLY;\nimport static nl.basjes.parse.core.Casts.STRING_OR_LONG;\nimport static nl.basjes.parse.httpdlog.Utils.makeHTMLEncodedInert;\n\npublic class HttpUriDissector extends Dissector {\n    // --------------------------------------------\n\n    private static final String INPUT_TYPE = \"HTTP.URI\";\n\n    @Override\n    public String getInputType() {\n        return INPUT_TYPE;\n    }\n\n    // --------------------------------------------\n\n    @Override\n    public List<String> getPossibleOutput() {\n        List<String> result = new ArrayList<>();\n        result.add(\"HTTP.PROTOCOL:protocol\");\n        result.add(\"HTTP.USERINFO:userinfo\");\n        result.add(\"HTTP.HOST:host\");\n        result.add(\"HTTP.PORT:port\");\n        result.add(\"HTTP.PATH:path\");\n        result.add(\"HTTP.QUERYSTRING:query\");\n        result.add(\"HTTP.REF:ref\");\n        return result;\n    }\n\n    // --------------------------------------------\n\n    private boolean wantProtocol = false;\n    private boolean wantUserinfo = false;\n    private boolean wantHost = false;\n    private boolean wantPort = false;\n    private boolean wantPath = false;\n    private boolean wantQuery = false;\n    private boolean wantRef = false;\n\n    @Override\n    public EnumSet<Casts> prepareForDissect(final String inputname, final String outputname) {\n        String name = extractFieldName(inputname, outputname);\n        if (\"protocol\".equals(name)) {\n            wantProtocol = true;\n            return STRING_ONLY;\n        }\n        if (\"userinfo\".equals(name)) {\n            wantUserinfo = true;\n            return STRING_ONLY;\n        }\n        if (\"host\".equals(name)) {\n            wantHost = true;\n            return STRING_ONLY;\n        }\n        if (\"port\".equals(name)) {\n            wantPort = true;\n            return STRING_OR_LONG;\n        }\n        if (\"path\".equals(name)) {\n            wantPath = true;\n            return STRING_ONLY;\n        }\n        if (\"query\".equals(name)) {\n            wantQuery = true;\n            return STRING_ONLY;\n        }\n        if (\"ref\".equals(name)) {\n            wantRef = true;\n            return STRING_ONLY;\n        }\n        return NO_CASTS;\n    }\n\n    // --------------------------------------------\n\n\n    // ---------------------------- Characters disallowed within the URI syntax\n    // Excluded US-ASCII Characters are like control, space, delims and unwise\n\n    private static final BitSet BAD_URI_CHARS = new BitSet(256);\n    static {\n        BAD_URI_CHARS.set(0, 255);\n        // Unwise\n        BAD_URI_CHARS.clear('{');\n        BAD_URI_CHARS.clear('}');\n        BAD_URI_CHARS.clear('|');\n        BAD_URI_CHARS.clear('\\\\');\n        BAD_URI_CHARS.clear('^');\n        BAD_URI_CHARS.clear('[');\n        BAD_URI_CHARS.clear(']');\n        BAD_URI_CHARS.clear('`');\n        // Space\n        BAD_URI_CHARS.clear(0x20);\n        // Control\n        BAD_URI_CHARS.clear(0, 0x1F);\n        BAD_URI_CHARS.clear(0x7F);\n        // Extra\n        BAD_URI_CHARS.clear('<');\n        BAD_URI_CHARS.clear('>');\n        BAD_URI_CHARS.clear('\"');\n    }\n\n    // Match % encoded chars that are NOT followed by hex chars (may be at the end of the string)\n    private static final Pattern BAD_EXCAPE_PATTERN = Pattern.compile(\"%([^0-9a-fA-F]|[0-9a-fA-F][^0-9a-fA-F]|.$|$)\");\n    private static final Pattern EQUALS_HASH_PATTERN = Pattern.compile(\"=#\");\n    private static final Pattern HASH_AMP_PATTERN = Pattern.compile(\"#&\");\n    private static final Pattern DOUBLE_HASH_PATTERN = Pattern.compile(\"#(.*)#\");\n    private static final Pattern ALMOST_HTML_ENCODED = Pattern.compile(\"([^&])(#x[0-9a-fA-F][0-9a-fA-F];)\");\n\n    @Override\n    public void dissect(final Parsable<?> parsable, final String inputname) throws DissectionFailure {\n        final ParsedField field = parsable.getParsableField(INPUT_TYPE, inputname);\n\n        String uriString = field.getValue().getString();\n        if (uriString == null || uriString.isEmpty()) {\n            return; // Nothing to do here\n        }\n\n        // First we cleanup the URI so we fail less often over 'garbage' URIs.\n        // See: https://stackoverflow.com/questions/11038967/brackets-in-a-request-url-are-legal-but-not-in-a-uri-java\n        uriString = new String(URLCodec.encodeUrl(BAD_URI_CHARS, uriString.getBytes(UTF_8)), US_ASCII);\n\n        // Now we translate any HTML encoded entities/characters into URL UTF-8 encoded characters\n        uriString = makeHTMLEncodedInert(uriString);\n\n        // Before we hand it to the standard parser we hack it around a bit so we can parse\n        // nasty edge cases that are illegal yet do occur in real clickstreams.\n        // Also we force the query string to start with ?& so the returned query string starts with &\n        // Which leads to more consistent output after parsing.\n        int firstQuestionMark = uriString.indexOf('?');\n        int firstAmpersand = uriString.indexOf('&');\n        // Now we can have one of 3 situations:\n        // 1) No query string\n        // 2) Query string starts with a '?'\n        //      (and optionally followed by one or more '&' or '?' )\n        // 3) Query string starts with a '&'. This is invalid but does occur!\n        // We may have ?x=x&y=y?z=z so we normalize it always\n        // to:  ?&x=x&y=y&z=z\n        if (firstAmpersand != -1 || firstQuestionMark != -1) {\n            uriString = uriString.replace(\"?\", \"&\");\n            uriString = uriString.replaceFirst(\"&\", \"?&\");\n        }\n\n        // We find that people muck up the URL by putting % signs in the URLs that are NOT escape sequences\n        // So any % that is not followed by a two 'hex' letters is fixed\n        uriString = BAD_EXCAPE_PATTERN.matcher(uriString).replaceAll(\"%25$1\");\n        uriString = BAD_EXCAPE_PATTERN.matcher(uriString).replaceAll(\"%25$1\");\n\n        // We have URIs with fragments like this:\n        //    /path/?_requestid=1234#x3D;12341234&Referrer&#x3D;blablabla\n        // So first we repair the broken encoded char\n        uriString = ALMOST_HTML_ENCODED.matcher(uriString).replaceAll(\"$1&$2\");\n        uriString = StringEscapeUtils.unescapeHtml4(uriString);\n        // And we see URIs with this:\n        //    /path/?Referrer=ADV1234#&f=API&subid=#&name=12341234\n        uriString = EQUALS_HASH_PATTERN.matcher(uriString).replaceAll(\"=\");\n        uriString = HASH_AMP_PATTERN.matcher(uriString).replaceAll(\"&\");\n\n        // If we still have multiple '#' in here we replace them with something else: '~'\n        while (true) {\n            Matcher doubleHashMatcher = DOUBLE_HASH_PATTERN.matcher(uriString);\n            if (!doubleHashMatcher.find()) {\n                break;\n            }\n            uriString = doubleHashMatcher.replaceAll(\"~$1#\");\n        }\n\n        boolean isUrl = true;\n        URI uri;\n        try {\n            if (uriString.charAt(0) == '/') {\n                uri = URI.create(\"dummy-protocol://dummy.host.name\" + uriString);\n                isUrl = false; // I.e. we do not return the values we just faked.\n            } else {\n                uri = URI.create(uriString);\n            }\n        } catch (IllegalArgumentException e) {\n            // A bad parsing error happened so no dissection results.\n            return;\n        }\n\n        if (wantQuery || wantPath || wantRef) {\n            if (wantQuery) {\n                String value = uri.getRawQuery();\n                if (value != null && !value.isEmpty()) {\n                    parsable.addDissection(inputname, \"HTTP.QUERYSTRING\", \"query\", value);\n                }\n            }\n            if (wantPath) {\n                String value = uri.getPath();\n                if (value != null && !value.isEmpty()) {\n                    parsable.addDissection(inputname, \"HTTP.PATH\", \"path\", value);\n                }\n            }\n            if (wantRef) {\n                String value = uri.getFragment();\n                if (value != null && !value.isEmpty()) {\n                    parsable.addDissection(inputname, \"HTTP.REF\", \"ref\", value);\n                }\n            }\n        }\n\n        if (isUrl) {\n            if (wantProtocol) {\n                String value = uri.getScheme();\n                if (value != null && !value.isEmpty()) {\n                    parsable.addDissection(inputname, \"HTTP.PROTOCOL\", \"protocol\", value);\n                }\n            }\n            if (wantUserinfo) {\n                String value = uri.getUserInfo();\n                if (value != null && !value.isEmpty()) {\n                    parsable.addDissection(inputname, \"HTTP.USERINFO\", \"userinfo\", value);\n                }\n            }\n            if (wantHost) {\n                String value = uri.getHost();\n                if (value != null && !value.isEmpty()) {\n                    parsable.addDissection(inputname, \"HTTP.HOST\", \"host\", value);\n                }\n            }\n            if (wantPort) {\n                int value = uri.getPort();\n                if (value != -1) {\n                    parsable.addDissection(inputname, \"HTTP.PORT\", \"port\", value);\n                }\n            }\n        }\n    }\n    // --------------------------------------------\n\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/main/java/nl/basjes/parse/httpdlog/dissectors/ModUniqueIdDissector.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.httpdlog.dissectors;\n\nimport nl.basjes.parse.core.Casts;\nimport nl.basjes.parse.core.Dissector;\nimport nl.basjes.parse.core.Parsable;\nimport nl.basjes.parse.core.ParsedField;\nimport nl.basjes.parse.core.exceptions.DissectionFailure;\nimport org.apache.commons.codec.binary.Base64;\n\nimport java.nio.charset.Charset;\nimport java.nio.charset.StandardCharsets;\nimport java.util.ArrayList;\nimport java.util.EnumSet;\nimport java.util.List;\n\nimport static nl.basjes.parse.core.Casts.NO_CASTS;\nimport static nl.basjes.parse.core.Casts.STRING_OR_LONG;\n\n/**\n * The documentation of mod_unique_id clearly states:\n * https://httpd.apache.org/docs/current/mod/mod_unique_id.html\n * ... it should be emphasized that applications should not dissect the encoding. ...\n * Applications should treat the entire encoded UNIQUE_ID as an opaque token,\n * which can be compared against other UNIQUE_IDs for equality only.\n *\n * Yet being able to peek inside is sometimes very useful...\n */\npublic class ModUniqueIdDissector extends Dissector {\n    // --------------------------------------------\n\n    private static final String INPUT_TYPE = \"MOD_UNIQUE_ID\";\n\n    @Override\n    public String getInputType() {\n        return INPUT_TYPE;\n    }\n\n    // --------------------------------------------\n\n    @Override\n    public List<String> getPossibleOutput() {\n        List<String> result = new ArrayList<>();\n\n        result.add(\"TIME.EPOCH:epoch\");\n        result.add(\"IP:ip\");\n        result.add(\"PROCESSID:processid\");\n        result.add(\"COUNTER:counter\");\n        result.add(\"THREAD_INDEX:threadindex\");\n        return result;\n    }\n\n    // --------------------------------------------\n\n    private boolean wantTime = false;\n    private boolean wantIp = false;\n    private boolean wantProcessId = false;\n    private boolean wantCounter = false;\n    private boolean wantThreadIndex = false;\n\n    @Override\n    public EnumSet<Casts> prepareForDissect(final String inputname, final String outputname) {\n        String name = extractFieldName(inputname, outputname);\n        if (\"epoch\".equals(name)) {\n            wantTime = true;\n            return STRING_OR_LONG;\n        }\n        if (\"ip\".equals(name)) {\n            wantIp = true;\n            return STRING_OR_LONG;\n        }\n        if (\"processid\".equals(name)) {\n            wantProcessId = true;\n            return STRING_OR_LONG;\n        }\n        if (\"counter\".equals(name)) {\n            wantCounter = true;\n            return STRING_OR_LONG;\n        }\n        if (\"threadindex\".equals(name)) {\n            wantThreadIndex = true;\n            return STRING_OR_LONG;\n        }\n        return NO_CASTS;\n    }\n\n    // --------------------------------------------\n\n    @Override\n    public void dissect(final Parsable<?> parsable, final String inputname) throws DissectionFailure {\n        final ParsedField field = parsable.getParsableField(INPUT_TYPE, inputname);\n\n        String fieldValue = field.getValue().getString();\n        if (fieldValue == null || fieldValue.isEmpty()) {\n            return; // Nothing to do here\n        }\n\n        UniqueIdRec uniqueIdRec = decode(fieldValue);\n        if (uniqueIdRec == null) {\n            return;\n        }\n\n        if (wantTime) {\n            parsable.addDissection(inputname, \"TIME.EPOCH\",   \"epoch\",       uniqueIdRec.timestamp);\n        }\n        if (wantIp) {\n            parsable.addDissection(inputname, \"IP\",           \"ip\",          uniqueIdRec.ipaddrStr);\n        }\n        if (wantProcessId) {\n            parsable.addDissection(inputname, \"PROCESSID\",    \"processid\",   uniqueIdRec.pid);\n        }\n        if (wantCounter) {\n            parsable.addDissection(inputname, \"COUNTER\",      \"counter\",     uniqueIdRec.counter);\n        }\n        if (wantThreadIndex) {\n            parsable.addDissection(inputname, \"THREAD_INDEX\", \"threadindex\", uniqueIdRec.threadIndex);\n        }\n    }\n    // --------------------------------------------\n\n    private static final class UniqueIdRec {\n        long timestamp;\n        long ipaddr;\n        String ipaddrStr;\n        long pid;\n        long counter;\n        long threadIndex;\n    }\n\n    // 1 letter = 6 bits of data = 2^6 = 64 letters needed to do the mapping\n    // 4 letters = 4*6 = 24 = 3*8 = 3 bytes\n    // So 24 letters = 24*6 = 144 bits = 18 bytes\n    public static final Charset CHARSET_UTF_8 = StandardCharsets.UTF_8;\n\n    private static final byte[] EMPTY = {};\n\n    private byte[] decodeToBytes(String modUniqueIdString) {\n        if (modUniqueIdString.length() != 24) {\n            return EMPTY;\n        }\n\n        // http://httpd.apache.org/docs/current/mod/mod_unique_id.html\n        // The UNIQUE_ID environment variable is constructed by encoding the 144-bit\n        // (32-bit IP address, 32 bit pid, 32 bit time stamp, 16 bit counter, 32 bit thread index)\n        // quadruple using the alphabet [A-Za-z0-9@-] in a manner similar to MIME base64 encoding,\n        // producing 24 characters.\n\n        // This implementation is based on the observation that the encoding used by mod-unique-id is\n        // the same as Base64 except that the last two letters are different.\n        // So by simply replacing the occurrences of these letters in the source we reuse and existing\n        // Base64 decode implementation.\n\n        byte[] modUniqueIdBytes = modUniqueIdString.getBytes(CHARSET_UTF_8);\n\n        byte[] modUniqueIdBase64Bytes = new byte[modUniqueIdBytes.length];\n\n        for (int i = 0; i < modUniqueIdBytes.length; i++) {\n            byte nextByte = modUniqueIdBytes[i];\n            switch (nextByte) {\n                case '+':\n                case '/':\n                    modUniqueIdBase64Bytes[i] = '@';\n                    break;\n\n                default:\n                    modUniqueIdBase64Bytes[i] = nextByte;\n                    break;\n            }\n        }\n\n        try {\n            return Base64.decodeBase64(modUniqueIdBase64Bytes);\n        } catch (IllegalArgumentException iae) {\n            return EMPTY;\n        }\n    }\n\n    private UniqueIdRec decode(String modUniqueIdString) {\n        byte[] bytes = decodeToBytes(modUniqueIdString);\n        if (bytes == EMPTY) {\n            return null;\n        }\n\n        // Is the decoded output the right length?\n        if (bytes.length != 18) {\n            return null;\n        }\n\n        UniqueIdRec result = new UniqueIdRec();\n\n//        http://httpd.apache.org/docs/current/mod/mod_unique_id.html\n\n//         we will use a Unix timestamp (seconds since January 1, 1970 UTC)\n//      (32-bit IP address, 32 bit pid, 32 bit time stamp, 16 bit counter, 32 bit thread index)\n//      The actual ordering of the encoding is: time stamp, IP address, pid, counter.\n\n        result.timestamp    =                               (bytes[0] & 0xFF);\n        result.timestamp    = (result.timestamp    * 256) + (bytes[1] & 0xFF);\n        result.timestamp    = (result.timestamp    * 256) + (bytes[2] & 0xFF);\n        result.timestamp    = (result.timestamp    * 256) + (bytes[3] & 0xFF);\n        // Quote: The timestamp has only one second granularity\n        result.timestamp   *= 1000; // This is to convert the time into milliseconds\n\n        // NOTE: In case of IPv6 the value will be related to the lower bits of the address.\n        result.ipaddr       =                               (bytes[4] & 0xFF);\n        result.ipaddr       = (result.ipaddr       * 256) + (bytes[5] & 0xFF);\n        result.ipaddr       = (result.ipaddr       * 256) + (bytes[6] & 0xFF);\n        result.ipaddr       = (result.ipaddr       * 256) + (bytes[7] & 0xFF);\n        result.ipaddrStr    = \"\"  + (bytes[4] & 0xFF) +\n                              '.' + (bytes[5] & 0xFF) +\n                              '.' + (bytes[6] & 0xFF) +\n                              '.' + (bytes[7] & 0xFF);\n\n        result.pid          =                                (bytes[8] & 0xFF);\n        result.pid          = (result.pid          * 256) +  (bytes[9] & 0xFF);\n        result.pid          = (result.pid          * 256) +  (bytes[10] & 0xFF);\n        result.pid          = (result.pid          * 256) +  (bytes[11] & 0xFF);\n\n        result.counter      =                                (bytes[12] & 0xFF);\n        result.counter      = (result.counter      * 256) +  (bytes[13] & 0xFF);\n\n        result.threadIndex  =                                (bytes[14] & 0xFF);\n        result.threadIndex  = (result.threadIndex  * 256) +  (bytes[15] & 0xFF);\n        result.threadIndex  = (result.threadIndex  * 256) +  (bytes[16] & 0xFF);\n        result.threadIndex  = (result.threadIndex  * 256) +  (bytes[17] & 0xFF);\n\n        return result;\n    }\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/main/java/nl/basjes/parse/httpdlog/dissectors/QueryStringFieldDissector.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.httpdlog.dissectors;\n\nimport nl.basjes.parse.core.Casts;\nimport nl.basjes.parse.core.Dissector;\nimport nl.basjes.parse.core.Parsable;\nimport nl.basjes.parse.core.ParsedField;\nimport nl.basjes.parse.core.exceptions.DissectionFailure;\n\nimport java.util.ArrayList;\nimport java.util.EnumSet;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport static nl.basjes.parse.core.Casts.STRING_ONLY;\nimport static nl.basjes.parse.httpdlog.Utils.resilientUrlDecode;\n\npublic class QueryStringFieldDissector extends Dissector {\n    // --------------------------------------------\n\n    private static final String INPUT_TYPE = \"HTTP.QUERYSTRING\";\n\n    @Override\n    public String getInputType() {\n        return INPUT_TYPE;\n    }\n\n    // --------------------------------------------\n\n    /** This should output all possible types */\n    @Override\n    public List<String> getPossibleOutput() {\n        List<String> result = new ArrayList<>();\n        result.add(\"STRING:*\");\n        return result;\n    }\n\n    // --------------------------------------------\n\n    private final Set<String> requestedParameters = new HashSet<>(16);\n\n    @Override\n    public EnumSet<Casts> prepareForDissect(final String inputname, final String outputname) {\n        requestedParameters.add(extractFieldName(inputname, outputname));\n        return STRING_ONLY;\n    }\n\n    // --------------------------------------------\n\n    private boolean wantAllFields = false;\n\n    @Override\n    public void prepareForRun() {\n        wantAllFields = requestedParameters.contains(\"*\");\n    }\n\n    // --------------------------------------------\n\n    @Override\n    public void dissect(final Parsable<?> parsable, final String inputname) throws DissectionFailure {\n        final ParsedField field = parsable.getParsableField(INPUT_TYPE, inputname);\n\n        String fieldValue = field.getValue().getString();\n        if (fieldValue == null || fieldValue.isEmpty()) {\n            return; // Nothing to do here\n        }\n\n        String[] allValues = fieldValue.split(\"&\");\n\n        for (String value : allValues) {\n            int equalPos = value.indexOf('=');\n            if (equalPos == -1) {\n                if (!value.isEmpty()) {\n                    String name = value.toLowerCase();\n                    if (wantAllFields || requestedParameters.contains(name)) {\n                        parsable.addDissection(inputname, \"STRING\", name, \"\");\n                    }\n                }\n            } else {\n                String name = value.substring(0, equalPos).toLowerCase();\n                if (wantAllFields || requestedParameters.contains(name)) {\n                    try {\n                        parsable.addDissection(inputname, \"STRING\", name,\n                                resilientUrlDecode(value.substring(equalPos + 1)));\n                    } catch (IllegalArgumentException e) {\n                        // This usually means that there was invalid encoding in the line\n                        throw new DissectionFailure(e.getMessage());\n                    }\n                }\n            }\n        }\n    }\n\n    // --------------------------------------------\n\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/main/java/nl/basjes/parse/httpdlog/dissectors/RequestCookieListDissector.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.httpdlog.dissectors;\n\nimport nl.basjes.parse.core.Casts;\nimport nl.basjes.parse.core.Dissector;\nimport nl.basjes.parse.core.Parsable;\nimport nl.basjes.parse.core.ParsedField;\nimport nl.basjes.parse.core.exceptions.DissectionFailure;\nimport nl.basjes.parse.httpdlog.Utils;\n\nimport java.util.ArrayList;\nimport java.util.EnumSet;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.regex.Pattern;\n\nimport static nl.basjes.parse.core.Casts.STRING_ONLY;\n\npublic class RequestCookieListDissector extends Dissector {\n    // --------------------------------------------\n\n    private static final String INPUT_TYPE = \"HTTP.COOKIES\";\n\n    @Override\n    public String getInputType() {\n        return INPUT_TYPE;\n    }\n\n    // --------------------------------------------\n\n    /** This should output all possible types */\n    @Override\n    public List<String> getPossibleOutput() {\n        List<String> result = new ArrayList<>();\n        result.add(\"HTTP.COOKIE:*\");\n        return result;\n    }\n\n    // --------------------------------------------\n\n    private final Set<String> requestedCookies = new HashSet<>(16);\n\n    @Override\n    public EnumSet<Casts> prepareForDissect(final String inputname, final String outputname) {\n        requestedCookies.add(extractFieldName(inputname, outputname));\n        return STRING_ONLY;\n    }\n\n    // --------------------------------------------\n    private boolean wantAllCookies = false;\n\n    @Override\n    public void prepareForRun() {\n        wantAllCookies = requestedCookies.contains(\"*\");\n    }\n\n    // --------------------------------------------\n\n    // Cache the compiled pattern\n    private final Pattern fieldSeparatorPattern = Pattern.compile(\"; \");\n\n    @Override\n    public void dissect(final Parsable<?> parsable, final String inputname) throws DissectionFailure {\n        final ParsedField field = parsable.getParsableField(INPUT_TYPE, inputname);\n\n        final String fieldValue = field.getValue().getString();\n        if (fieldValue == null || fieldValue.isEmpty()){\n            return; // Nothing to do here\n        }\n\n        String[] allValues = fieldSeparatorPattern.split(fieldValue);\n        for (String value : allValues) {\n            int equalPos = value.indexOf('=');\n            if (equalPos == -1) {\n                if (!\"\".equals(value)) {\n                    String theName = value.trim().toLowerCase(); // Just a name, no value\n                    if (wantAllCookies || requestedCookies.contains(theName)) {\n                        parsable.addDissection(inputname, \"HTTP.COOKIE\", theName, \"\");\n                    }\n                }\n            } else {\n                String theName = value.substring(0, equalPos).trim().toLowerCase();\n                if (wantAllCookies || requestedCookies.contains(theName)) {\n                    String theValue = value.substring(equalPos + 1).trim();\n                    try {\n                        parsable.addDissection(inputname, \"HTTP.COOKIE\", theName,\n                                Utils.resilientUrlDecode(theValue));\n                    } catch (IllegalArgumentException e) {\n                        // This usually means that there was invalid encoding in the line\n                        throw new DissectionFailure(e.getMessage());\n                    }\n                }\n            }\n        }\n    }\n\n    // --------------------------------------------\n\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/main/java/nl/basjes/parse/httpdlog/dissectors/ResponseSetCookieDissector.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.httpdlog.dissectors;\n\nimport nl.basjes.parse.core.Casts;\nimport nl.basjes.parse.core.Dissector;\nimport nl.basjes.parse.core.Parsable;\nimport nl.basjes.parse.core.ParsedField;\nimport nl.basjes.parse.core.exceptions.DissectionFailure;\n\nimport java.time.ZoneOffset;\nimport java.time.ZonedDateTime;\nimport java.time.format.DateTimeFormatter;\nimport java.util.ArrayList;\nimport java.util.EnumSet;\nimport java.util.List;\n\nimport static nl.basjes.parse.core.Casts.STRING_ONLY;\nimport static nl.basjes.parse.core.Casts.STRING_OR_LONG;\n\npublic class ResponseSetCookieDissector extends Dissector {\n    // --------------------------------------------\n\n    private static final String INPUT_TYPE = \"HTTP.SETCOOKIE\";\n\n    @Override\n    public String getInputType() {\n        return INPUT_TYPE;\n    }\n\n    // --------------------------------------------\n\n    /** This should output all possible types */\n    @Override\n    public List<String> getPossibleOutput() {\n        List<String> result = new ArrayList<>();\n        result.add(\"STRING:value\");\n        result.add(\"STRING:expires\");\n        result.add(\"TIME.EPOCH:expires\");\n        result.add(\"STRING:path\");\n        result.add(\"STRING:domain\");\n        result.add(\"STRING:comment\");\n        return result;\n    }\n\n    // --------------------------------------------\n\n    @Override\n    public EnumSet<Casts> prepareForDissect(final String inputname, final String outputname) {\n        String name = extractFieldName(inputname, outputname);\n        switch (name) {\n            case \"expires\":\n                return STRING_OR_LONG;\n            case \"value\":\n            case \"path\":\n            case \"domain\":\n            case \"comment\":\n            default:\n                return STRING_ONLY;\n        }\n    }\n\n    // --------------------------------------------\n\n    @Override\n    public void dissect(final Parsable<?> parsable, final String inputname) throws DissectionFailure {\n        final ParsedField field = parsable.getParsableField(INPUT_TYPE, inputname);\n\n        final String fieldValue = field.getValue().getString();\n        if (fieldValue == null || fieldValue.isEmpty()){\n            return; // Nothing to do here\n        }\n\n        String[] parts = fieldValue.split(\";\");\n\n        for (int i = 0; i < parts.length; i++) {\n            String part = parts[i].trim();\n            String[] keyValue = part.split(\"=\", 2);\n\n            String key = keyValue[0].trim();\n            String value = \"\";\n            if (keyValue.length == 2) {\n                value = keyValue[1].trim();\n            }\n\n            if (i==0) {\n                parsable.addDissection(inputname, \"STRING\", \"value\", value);\n            } else {\n                switch (key) {\n                    // We ignore the max-age field because that is unsupported by IE anyway.\n                    case \"expires\":\n                        Long expires = parseExpire(value);\n                        // Backwards compatibility: STRING version is in seconds\n                        parsable.addDissection(inputname, \"STRING\",     \"expires\", expires / 1000);\n                        parsable.addDissection(inputname, \"TIME.EPOCH\", \"expires\", expires);\n                        break;\n                    case \"domain\":\n                        parsable.addDissection(inputname, \"STRING\", \"domain\",   value);\n                        break;\n                    case \"comment\":\n                        parsable.addDissection(inputname, \"STRING\", \"comment\",  value);\n                        break;\n                    case \"path\":\n                        parsable.addDissection(inputname, \"STRING\", \"path\",     value);\n                        break;\n                    default: // Ignore anything else\n                }\n            }\n        }\n    }\n\n    // --------------------------------------------\n\n    private static final DateTimeFormatter[] DATE_FORMATS = {\n        DateTimeFormatter.ofPattern(\"EEE',' dd-MMM-yyyy HH:mm:ss zzz\").withZone(ZoneOffset.UTC),\n        DateTimeFormatter.ofPattern(\"EEE',' dd MMM yyyy HH:mm:ss zzz\").withZone(ZoneOffset.UTC),\n        DateTimeFormatter.ofPattern(\"EEE MMM dd yyyy HH:mm:ss 'GMT'Z\")  .withZone(ZoneOffset.UTC),\n    };\n\n    private Long parseExpire(String expireString) {\n        for (DateTimeFormatter dateFormat: DATE_FORMATS) {\n            try {\n                return dateFormat.parse(expireString, ZonedDateTime::from).toEpochSecond() * 1000;\n            } catch (IllegalArgumentException iae) {\n                // Ignore and continue\n            }\n        }\n        return 0L;\n    }\n\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/main/java/nl/basjes/parse/httpdlog/dissectors/ResponseSetCookieListDissector.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.httpdlog.dissectors;\n\nimport nl.basjes.parse.core.Casts;\nimport nl.basjes.parse.core.Dissector;\nimport nl.basjes.parse.core.Parsable;\nimport nl.basjes.parse.core.ParsedField;\nimport nl.basjes.parse.core.exceptions.DissectionFailure;\n\nimport java.net.HttpCookie;\nimport java.util.ArrayList;\nimport java.util.EnumSet;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport static nl.basjes.parse.core.Casts.STRING_ONLY;\n\npublic class ResponseSetCookieListDissector extends Dissector {\n    // --------------------------------------------\n\n    private static final String INPUT_TYPE = \"HTTP.SETCOOKIES\";\n\n    @Override\n    public String getInputType() {\n        return INPUT_TYPE;\n    }\n\n    // --------------------------------------------\n\n    /** This should output all possible types */\n    @Override\n    public List<String> getPossibleOutput() {\n        List<String> result = new ArrayList<>();\n        result.add(\"HTTP.SETCOOKIE:*\");\n        return result;\n    }\n\n    // --------------------------------------------\n    private final Set<String> requestedCookies = new HashSet<>(16);\n\n    @Override\n    public EnumSet<Casts> prepareForDissect(final String inputname, final String outputname) {\n        requestedCookies.add(extractFieldName(inputname, outputname));\n        return STRING_ONLY;\n    }\n\n    // --------------------------------------------\n\n    private boolean wantAllCookies = false;\n\n    @Override\n    public void prepareForRun() {\n        wantAllCookies = requestedCookies.contains(\"*\");\n    }\n\n    // --------------------------------------------\n\n    private final int minimalExpiresLength = \"expires=XXXXXXX\".length();\n\n    // Cache the compiled pattern\n    private static final String SPLIT_BY = \", \";\n\n    @Override\n    public void dissect(final Parsable<?> parsable, final String inputname) throws DissectionFailure {\n        final ParsedField field = parsable.getParsableField(INPUT_TYPE, inputname);\n\n        final String fieldValue = field.getValue().getString();\n        if (fieldValue == null || fieldValue.isEmpty()){\n            return; // Nothing to do here\n        }\n\n        // This input is a ', ' separated list.\n        // But the expires field can contain a ','\n        // and HttpCookie.parse(...) doesn't always work :(\n        String[] parts = fieldValue.split(SPLIT_BY);\n\n        String previous=\"\";\n        for (String part:parts) {\n            int expiresIndex = part.toLowerCase().indexOf(\"expires=\");\n            if (expiresIndex != -1) {\n                if (part.length() - minimalExpiresLength < expiresIndex) {\n                    previous = part;\n                    continue;\n                }\n            }\n            String value = part;\n            if (!previous.isEmpty()) {\n                value = previous+ SPLIT_BY +part;\n                previous=\"\";\n            }\n\n            List<HttpCookie> cookies = HttpCookie.parse(value);\n\n            for (HttpCookie cookie : cookies) {\n                cookie.setVersion(1);\n                String cookieName = cookie.getName().toLowerCase();\n                if (wantAllCookies || requestedCookies.contains(cookieName)) {\n                    parsable.addDissection(inputname, \"HTTP.SETCOOKIE\", cookieName, value);\n                }\n            }\n        }\n\n    }\n\n    // --------------------------------------------\n\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/main/java/nl/basjes/parse/httpdlog/dissectors/ScreenResolutionDissector.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.httpdlog.dissectors;\n\nimport nl.basjes.parse.core.Casts;\nimport nl.basjes.parse.core.Dissector;\nimport nl.basjes.parse.core.Parsable;\nimport nl.basjes.parse.core.ParsedField;\nimport nl.basjes.parse.core.exceptions.DissectionFailure;\n\nimport java.util.ArrayList;\nimport java.util.EnumSet;\nimport java.util.List;\n\nimport static nl.basjes.parse.core.Casts.NO_CASTS;\nimport static nl.basjes.parse.core.Casts.STRING_OR_LONG;\n\npublic class ScreenResolutionDissector extends Dissector {\n\n    public static final String SCREENRESOLUTION = \"SCREENRESOLUTION\";\n    private String separator = \"x\";\n    private boolean wantWidth = false;\n    private boolean wantHeight = false;\n\n    @Override\n    public boolean initializeFromSettingsParameter(String settings) {\n        if (settings.length() > 0) {\n            this.separator = settings;\n        }\n        return true;\n    }\n\n    @Override\n    public void dissect(Parsable<?> parsable, String inputname) throws DissectionFailure {\n        final ParsedField field = parsable.getParsableField(SCREENRESOLUTION, inputname);\n\n        final String fieldValue = field.getValue().getString();\n        if (fieldValue == null || fieldValue.isEmpty()) {\n            return; // Nothing to do here\n        }\n\n        if (fieldValue.contains(separator)) {\n            String[] parts = fieldValue.split(separator);\n            if (wantWidth) {\n                parsable.addDissection(inputname, \"SCREENWIDTH\", \"width\", parts[0]);\n            }\n            if (wantHeight) {\n                parsable.addDissection(inputname, \"SCREENHEIGHT\", \"height\", parts[1]);\n            }\n        }\n    }\n\n    @Override\n    public String getInputType() {\n        return SCREENRESOLUTION;\n    }\n\n    @Override\n    public List<String> getPossibleOutput() {\n        List<String> result = new ArrayList<>();\n        result.add(\"SCREENWIDTH:width\");\n        result.add(\"SCREENHEIGHT:height\");\n        return result;\n    }\n\n    @Override\n    public EnumSet<Casts> prepareForDissect(String inputname, String outputname) {\n        String name = extractFieldName(inputname, outputname);\n        if (\"width\".equals(name)) {\n            wantWidth = true;\n            return STRING_OR_LONG;\n        }\n        if (\"height\".equals(name)) {\n            wantHeight = true;\n            return STRING_OR_LONG;\n        }\n        return NO_CASTS;\n    }\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/main/java/nl/basjes/parse/httpdlog/dissectors/StrfTimeStampDissector.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.httpdlog.dissectors;\n\nimport nl.basjes.parse.core.Casts;\nimport nl.basjes.parse.core.Dissector;\nimport nl.basjes.parse.core.Parsable;\nimport nl.basjes.parse.core.ParsedField;\nimport nl.basjes.parse.core.Parser;\nimport nl.basjes.parse.core.exceptions.DissectionFailure;\nimport nl.basjes.parse.core.exceptions.InvalidDissectorException;\n\nimport java.util.ArrayList;\nimport java.util.EnumSet;\nimport java.util.List;\n\nimport static nl.basjes.parse.core.Casts.STRING_ONLY;\n\npublic class StrfTimeStampDissector extends Dissector {\n\n    private final TimeStampDissector timeStampDissector;\n\n    private String strfDateTimePattern = null;\n    private String inputType = \"TIME.?????\";\n\n    public StrfTimeStampDissector() {\n        timeStampDissector = new TimeStampDissector();\n    }\n\n    public void setDateTimePattern(String newDateTimePattern) {\n        if (newDateTimePattern == null) {\n            timeStampDissector.setDateTimePattern(\"\");\n            return; // Done\n        }\n\n        if (newDateTimePattern.equals(strfDateTimePattern)) {\n            return; // Nothing to do\n        }\n\n        this.strfDateTimePattern = newDateTimePattern;\n        timeStampDissector.setFormatter(StrfTimeToDateTimeFormatter.convert(newDateTimePattern));\n    }\n\n    @Override\n    public boolean initializeFromSettingsParameter(String settings) {\n        setDateTimePattern(settings);\n        return true;\n    }\n\n    @Override\n    public void dissect(Parsable<?> parsable, String inputname) throws DissectionFailure {\n        final ParsedField field = parsable.getParsableField(inputType, inputname);\n        timeStampDissector.dissect(field, parsable, inputname);\n    }\n\n    @Override\n    public String getInputType() {\n        return inputType;\n    }\n\n    @Override\n    public List<String> getPossibleOutput() {\n        return timeStampDissector.getPossibleOutput();\n    }\n\n    @Override\n    public EnumSet<Casts> prepareForDissect(String inputname, String outputname) {\n        return timeStampDissector.prepareForDissect(inputname, outputname);\n    }\n\n    @Override\n    public void prepareForRun() {\n        timeStampDissector.prepareForRun();\n    }\n\n    @Override\n    protected void initializeNewInstance(Dissector newInstance) {\n        StrfTimeStampDissector newStrfTimeStampDissector = (StrfTimeStampDissector) newInstance;\n        newStrfTimeStampDissector.timeStampDissector.initializeNewInstance(newStrfTimeStampDissector.timeStampDissector);\n        newStrfTimeStampDissector.setInputType(inputType);\n        newStrfTimeStampDissector.setDateTimePattern(strfDateTimePattern);\n    }\n\n    @Override\n    public void setInputType(String newInputType) {\n        inputType = newInputType;\n    }\n\n    @Override\n    public <RECORD> void createAdditionalDissectors(Parser<RECORD> parser) {\n        parser.addDissector(new LocalizedTimeDissector(inputType));\n    }\n\n    public static class LocalizedTimeDissector extends Dissector {\n\n        String inputType = null;\n\n        public LocalizedTimeDissector() {\n        }\n\n        public LocalizedTimeDissector(String inputType) {\n            this.inputType = inputType;\n        }\n\n        @Override\n        public void setInputType(String newInputType) {\n            inputType = newInputType;\n        }\n\n        @Override\n        public boolean initializeFromSettingsParameter(String settings) {\n            setInputType(settings);\n            return true;\n        }\n\n        @Override\n        public void dissect(Parsable<?> parsable, String inputname) throws DissectionFailure {\n            final ParsedField field = parsable.getParsableField(inputType, inputname);\n            parsable.addDissection(inputname, \"TIME.LOCALIZEDSTRING\", \"\", field.getValue());\n        }\n\n        @Override\n        public String getInputType() {\n            return inputType;\n        }\n\n        @Override\n        public List<String> getPossibleOutput() {\n            List<String> result = new ArrayList<>();\n            result.add(\"TIME.LOCALIZEDSTRING:\");\n            return result;\n        }\n\n        @Override\n        public EnumSet<Casts> prepareForDissect(String inputname, String outputname) {\n            return STRING_ONLY;\n        }\n\n        @Override\n        protected void initializeNewInstance(Dissector newInstance) throws InvalidDissectorException {\n            newInstance.setInputType(inputType);\n        }\n    }\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/main/java/nl/basjes/parse/httpdlog/dissectors/StrfTimeToDateTimeFormatter.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.httpdlog.dissectors;\n\nimport nl.basjes.parse.strftime.StrfTimeBaseListener;\nimport nl.basjes.parse.strftime.StrfTimeLexer;\nimport nl.basjes.parse.strftime.StrfTimeParser;\nimport org.antlr.v4.runtime.ANTLRErrorListener;\nimport org.antlr.v4.runtime.CharStreams;\nimport org.antlr.v4.runtime.CodePointCharStream;\nimport org.antlr.v4.runtime.CommonTokenStream;\nimport org.antlr.v4.runtime.RecognitionException;\nimport org.antlr.v4.runtime.Recognizer;\nimport org.antlr.v4.runtime.atn.ATNConfigSet;\nimport org.antlr.v4.runtime.dfa.DFA;\nimport org.antlr.v4.runtime.tree.ParseTreeWalker;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.time.ZoneId;\nimport java.time.ZoneOffset;\nimport java.time.format.DateTimeFormatter;\nimport java.time.format.DateTimeFormatterBuilder;\nimport java.time.format.SignStyle;\nimport java.time.format.TextStyle;\nimport java.time.temporal.ChronoField;\nimport java.time.temporal.WeekFields;\nimport java.util.BitSet;\nimport java.util.HashMap;\nimport java.util.Locale;\nimport java.util.Map;\n\npublic final class StrfTimeToDateTimeFormatter extends StrfTimeBaseListener implements ANTLRErrorListener {\n\n    private static final Logger LOG = LoggerFactory.getLogger(StrfTimeToDateTimeFormatter.class);\n\n    private static final WeekFields LOCAL_WEEK_FIELDS = WeekFields.of(Locale.getDefault());\n\n    public static DateTimeFormatter convert(String strfformat) {\n        return convert(strfformat, ZoneOffset.UTC);\n    }\n\n    public static DateTimeFormatter convert(String strfformat, ZoneId defaultZone) {\n        CodePointCharStream input = CharStreams.fromString(strfformat);\n        StrfTimeLexer lexer = new StrfTimeLexer(input);\n\n        CommonTokenStream tokens = new CommonTokenStream(lexer);\n\n        StrfTimeParser parser = new StrfTimeParser(tokens);\n\n        lexer.removeErrorListeners();\n        parser.removeErrorListeners();\n\n        ParseTreeWalker walker = new ParseTreeWalker(); // create standard walker\n        StrfTimeToDateTimeFormatter converter = new StrfTimeToDateTimeFormatter(strfformat, defaultZone);\n\n        lexer.addErrorListener(converter);\n        parser.addErrorListener(converter);\n\n        StrfTimeParser.PatternContext pattern = parser.pattern();\n\n        walker.walk(converter, pattern); // initiate walk of tree with listener\n\n        if (converter.hasSyntaxError()) {\n            return null;\n        }\n\n        return converter.build();\n    }\n\n    private final String strfformat;\n    private final DateTimeFormatterBuilder builder;\n    private final ZoneId defaultZone;\n    private boolean zoneWasSpecified = false;\n\n    private StrfTimeToDateTimeFormatter(String inputStrfformat, ZoneId newDefaultZone) {\n        strfformat = inputStrfformat;\n        defaultZone = newDefaultZone;\n        builder = new DateTimeFormatterBuilder()\n            .parseCaseInsensitive();\n    }\n\n    public DateTimeFormatter build() {\n        DateTimeFormatter dateTimeFormatter = builder.toFormatter();\n        if (!zoneWasSpecified) {\n            dateTimeFormatter = dateTimeFormatter.withZone(defaultZone);\n            LOG.warn(\"The timestamp format \\\"{}\\\" does NOT contain a timezone so we assume \\\"{}\\\".\",\n                strfformat, defaultZone.getDisplayName(TextStyle.SHORT, Locale.ENGLISH));\n        }\n        return dateTimeFormatter;\n    }\n\n    // ------------- Error handling --------------\n    private boolean syntaxError = false;\n\n    public boolean hasSyntaxError() {\n        return syntaxError;\n    }\n\n    @Override\n    public void syntaxError(Recognizer<?, ?> recognizer, Object o, int i, int i1, String s, RecognitionException e) {\n        syntaxError = true;\n    }\n\n    @Override\n    public void reportAmbiguity(org.antlr.v4.runtime.Parser parser, DFA dfa, int i, int i1, boolean b, BitSet bitSet, ATNConfigSet atnConfigSet) {\n        // Ignoring this Antlr4 event\n    }\n\n    @Override\n    public void reportAttemptingFullContext(org.antlr.v4.runtime.Parser parser, DFA dfa, int i, int i1, BitSet bitSet, ATNConfigSet atnConfigSet) {\n        // Ignoring this Antlr4 event\n    }\n\n    @Override\n    public void reportContextSensitivity(org.antlr.v4.runtime.Parser parser, DFA dfa, int i, int i1, int i2, ATNConfigSet atnConfigSet) {\n        // Ignoring this Antlr4 event\n    }\n\n    public static class UnsupportedStrfField extends RuntimeException {\n        public UnsupportedStrfField(String s) {\n            super(\"The field '\" + s + \"' cannot be converted towards a DateTimeFormatter field.\");\n        }\n    }\n\n    // ------------- Mapping --------------\n\n    @Override\n    public void enterMsecFrac(StrfTimeParser.MsecFracContext ctx) {\n        // Apache HTTPD specific: milliseconds fraction\n        builder.appendValue(ChronoField.MILLI_OF_SECOND, 3);\n    }\n\n    @Override\n    public void enterUsecFrac(StrfTimeParser.UsecFracContext ctx) {\n        // Apache HTTPD specific: microseconds fraction\n        builder.appendValue(ChronoField.MICRO_OF_SECOND, 6);\n    }\n\n    @Override\n    public void enterText(StrfTimeParser.TextContext ctx) {\n        builder.appendLiteral(ctx.getText());\n    }\n\n    @Override\n    public void enterTab(StrfTimeParser.TabContext ctx) {\n        builder.appendLiteral('\\t');\n    }\n\n    @Override\n    public void enterPercent(StrfTimeParser.PercentContext ctx) {\n        builder.appendLiteral('%');\n    }\n\n    @Override\n    public void enterNewline(StrfTimeParser.NewlineContext ctx) {\n        builder.appendLiteral('\\n');\n    }\n\n    @Override\n    public void enterPa(StrfTimeParser.PaContext ctx) {\n        // %a   The abbreviated name of the day of the week according to the current locale.\n        builder.appendText(ChronoField.DAY_OF_WEEK, TextStyle.SHORT);\n    }\n\n    @Override\n    public void enterPA(StrfTimeParser.PAContext ctx) {\n        // %A   The full name of the day of the week according to the current locale.\n        builder.appendText(ChronoField.DAY_OF_WEEK, TextStyle.FULL);\n    }\n\n    @Override\n    public void enterPb(StrfTimeParser.PbContext ctx) {\n        // %b   The abbreviated month name according to the current locale.\n        // %h   Equivalent to %b.\n        builder.appendText(ChronoField.MONTH_OF_YEAR, TextStyle.SHORT);\n    }\n\n    @Override\n    public void enterPB(StrfTimeParser.PBContext ctx) {\n        // %B   The full month name according to the current locale.\n        builder.appendText(ChronoField.MONTH_OF_YEAR, TextStyle.FULL);\n    }\n\n    @Override\n    public void enterPc(StrfTimeParser.PcContext ctx) {\n        // %c   The preferred date and time representation for the current locale.\n        throw new UnsupportedStrfField(\"%c   The preferred date and time representation for the current locale.\");\n    }\n\n    @Override\n    public void enterPC(StrfTimeParser.PCContext ctx) {\n        throw new UnsupportedStrfField(\"%C   The century number (year/100) as a 2-digit integer.\");\n    }\n\n    @Override\n    public void enterPd(StrfTimeParser.PdContext ctx) {\n        // %d   The day of the month as a decimal number (range 01 to 31).\n        builder.appendValue(ChronoField.DAY_OF_MONTH, 2);\n    }\n\n    @Override\n    public void enterPD(StrfTimeParser.PDContext ctx) {\n        // %D   Equivalent to %m/%d/%y. (Yecch—for Americans only)\n        builder\n            .appendValue(ChronoField.MONTH_OF_YEAR, 2)\n            .appendLiteral('/')\n            .appendValue(ChronoField.DAY_OF_MONTH, 2)\n            .appendLiteral('/')\n            .appendValueReduced(ChronoField.YEAR, 2, 2, 2000);\n    }\n\n    @Override\n    public void enterPe(StrfTimeParser.PeContext ctx) {\n        // %e   Like %d, the day of the month as a decimal number, but a leading zero is replaced by a space.\n        builder.padNext(2, ' ').appendValue(ChronoField.DAY_OF_MONTH);\n    }\n\n    @Override\n    public void enterPF(StrfTimeParser.PFContext ctx) {\n        // %F   Equivalent to %Y-%m-%d (the ISO 8601 date format).\n        builder\n            .appendValue(ChronoField.YEAR, 4)\n            .appendLiteral('-')\n            .appendValue(ChronoField.MONTH_OF_YEAR, 2)\n            .appendLiteral('-')\n            .appendValue(ChronoField.DAY_OF_MONTH, 2);\n    }\n\n    @Override\n    public void enterPG(StrfTimeParser.PGContext ctx) {\n        // %G   The ISO 8601 week-based year (see NOTES) with century as a decimal number.\n        //      The 4-digit year corresponding to the ISO week number (see %V).\n        //      This has the same format and value as %Y, except that if the ISO week number\n        //      belongs to the previous or next year, that year is used instead.\n        builder.appendValue(LOCAL_WEEK_FIELDS.weekBasedYear(), 4);\n    }\n\n    @Override\n    public void enterPg(StrfTimeParser.PgContext ctx) {\n        // %g   Like %G, but without century, that is, with a 2-digit year (00–99).\n        builder.appendValueReduced(LOCAL_WEEK_FIELDS.weekBasedYear(), 2, 2, 2000);\n    }\n\n    @Override\n    public void enterPH(StrfTimeParser.PHContext ctx) {\n        // %H   The hour as a decimal number using a 24-hour clock (range 00 to 23).\n        builder.appendValue(ChronoField.CLOCK_HOUR_OF_DAY, 2);\n    }\n\n    @Override\n    public void enterPI(StrfTimeParser.PIContext ctx) {\n        // %I   The hour as a decimal number using a 12-hour clock (range 01 to 12).\n        builder.appendValue(ChronoField.CLOCK_HOUR_OF_AMPM, 2);\n    }\n\n    @Override\n    public void enterPj(StrfTimeParser.PjContext ctx) {\n        // %j   The day of the year as a decimal number (range 001 to 366).\n        builder.appendValue(ChronoField.DAY_OF_YEAR, 3);\n    }\n\n    @Override\n    public void enterPk(StrfTimeParser.PkContext ctx) {\n        // %k   The hour (24-hour clock) as a decimal number (range 0 to 23); single digits are preceded by a blank.\n        //      (See also %H)\n        builder.padNext(2, ' ').appendValue(ChronoField.CLOCK_HOUR_OF_DAY);\n    }\n\n    @Override\n    public void enterPl(StrfTimeParser.PlContext ctx) {\n        // %l   The hour (12-hour clock) as a decimal number (range 1 to 12); single digits are preceded by a blank.\n        //      (See also %I)\n        builder.padNext(2, ' ').appendValue(ChronoField.CLOCK_HOUR_OF_AMPM);\n    }\n\n    @Override\n    public void enterPm(StrfTimeParser.PmContext ctx) {\n        // %m   The month as a decimal number (range 01 to 12).\n        builder.appendValue(ChronoField.MONTH_OF_YEAR, 2);\n    }\n\n    @Override\n    public void enterPM(StrfTimeParser.PMContext ctx) {\n        // %M   The minute as a decimal number (range 00 to 59).\n        builder.appendValue(ChronoField.MINUTE_OF_HOUR, 2);\n    }\n\n    @Override\n    public void enterPp(StrfTimeParser.PpContext ctx) {\n        // %p   Either \"AM\" or \"PM\" according to the given time value, or the corresponding strings for the current locale.\n        // Noon is treated as \"PM\" and midnight as \"AM\".\n        builder.appendText(ChronoField.AMPM_OF_DAY, TextStyle.SHORT);\n    }\n\n    private static final Map<Long, String> AMPM_LOWER_CASE_MAPPING = new HashMap<>();\n    static {\n        AMPM_LOWER_CASE_MAPPING.put(0L, \"am\");\n        AMPM_LOWER_CASE_MAPPING.put(1L, \"pm\");\n    }\n\n    @Override\n    public void enterPP(StrfTimeParser.PPContext ctx) {\n        // %P   Like %p but in lowercase: \"am\" or \"pm\" or a corresponding string for the current locale.\n        builder.appendText(ChronoField.AMPM_OF_DAY, AMPM_LOWER_CASE_MAPPING);\n    }\n\n    @Override\n    public void enterPr(StrfTimeParser.PrContext ctx) {\n        // %r   The time in a.m. or p.m. notation. In the POSIX locale this is equivalent to %I:%M:%S %p.\n        builder\n            .appendValue(ChronoField.CLOCK_HOUR_OF_AMPM, 2)\n            .appendLiteral(':')\n            .appendValue(ChronoField.MINUTE_OF_HOUR, 2)\n            .appendLiteral(':')\n            .appendValue(ChronoField.SECOND_OF_MINUTE, 2)\n            .appendLiteral(' ')\n            .appendText(ChronoField.AMPM_OF_DAY, TextStyle.SHORT);\n    }\n\n    @Override\n    public void enterPR(StrfTimeParser.PRContext ctx) {\n        // %R   The time in 24-hour notation (%H:%M). For a version including the seconds, see %T below.\n        builder\n            .appendValue(ChronoField.HOUR_OF_DAY, 2)\n            .appendLiteral(':')\n            .appendValue(ChronoField.MINUTE_OF_HOUR, 2);\n    }\n\n    @Override\n    public void enterPs(StrfTimeParser.PsContext ctx) {\n        // %s   The number of seconds since the Epoch, 1970-01-01 00:00:00 +0000 (UTC).\n        // Based upon https://stackoverflow.com/questions/36066155/datetimeformatter-for-epoch-milliseconds#answer-36069732\n        builder\n            .appendValue(ChronoField.INSTANT_SECONDS, 1, 19, SignStyle.NEVER);\n    }\n\n    @Override\n    public void enterPS(StrfTimeParser.PSContext ctx) {\n        // %S   The second as a decimal number (range 00 to 60). (The range is up to 60 to allow for occasional leap seconds)\n        builder\n            .appendValue(ChronoField.SECOND_OF_MINUTE, 2);\n    }\n\n    @Override\n    public void enterPT(StrfTimeParser.PTContext ctx) {\n        // %T   The time in 24-hour notation (%H:%M:%S).\n        builder\n            .appendValue(ChronoField.HOUR_OF_DAY, 2)\n            .appendLiteral(':')\n            .appendValue(ChronoField.MINUTE_OF_HOUR, 2)\n            .appendLiteral(':')\n            .appendValue(ChronoField.SECOND_OF_MINUTE, 2);\n    }\n\n    @Override\n    public void enterPu(StrfTimeParser.PuContext ctx) {\n        // %u   The day of the week as a decimal, range 1 to 7, Monday being 1. See also %w.\n        builder.appendValue(WeekFields.ISO.dayOfWeek(), 1);\n    }\n\n    @Override\n    public void enterPU(StrfTimeParser.PUContext ctx) {\n        // %U   The week number of the current year as a decimal number, range 00 to 53, starting with\n        //      the first Sunday as the first day of week 01. See also %V and %W.\n        throw new UnsupportedStrfField(\"%U The week number of the current year ... \");\n    }\n\n    @Override\n    public void enterPV(StrfTimeParser.PVContext ctx) {\n        // %V   The ISO 8601 week number (see NOTES) of the current year as a decimal number, range 01 to 53,\n        // where week 1 is the first week that has at least 4 days in the new year. See also %U and %W.\n        builder.appendValue(WeekFields.ISO.weekOfYear());\n    }\n\n    @Override\n    public void enterPw(StrfTimeParser.PwContext ctx) {\n        // %w   The day of the week as a decimal, range 0 to 6, Sunday being 0. See also %u.\n        throw new UnsupportedStrfField(\"%w   The day of the week as a decimal, range 0 to 6, Sunday being 0. See also %u.\");\n    }\n\n    @Override\n    public void enterPW(StrfTimeParser.PWContext ctx) {\n        // %W   The week number of the current year as a decimal number, range 00 to 53,\n        //      starting with the first Monday as the first day of week 01.\n        builder.appendValue(WeekFields.ISO.weekOfYear(), 2);\n    }\n\n    @Override\n    public void enterPx(StrfTimeParser.PxContext ctx) {\n        // %x   The preferred date representation for the current locale without the time.\n        throw new UnsupportedStrfField(\"%x   The preferred date representation for the current locale without the time.\");\n    }\n\n    @Override\n    public void enterPX(StrfTimeParser.PXContext ctx) {\n        // %X   The preferred time representation for the current locale without the date.\n        throw new UnsupportedStrfField(\"%X   The preferred time representation for the current locale without the date.\");\n    }\n\n    @Override\n    public void enterPy(StrfTimeParser.PyContext ctx) {\n        // %y   The year as a decimal number without a century (range 00 to 99).\n        builder.appendValueReduced(ChronoField.YEAR, 2, 2, 2000);\n    }\n\n    @Override\n    public void enterPY(StrfTimeParser.PYContext ctx) {\n        // %Y   The year as a decimal number including the century.\n        builder.appendValue(ChronoField.YEAR, 4);\n    }\n\n    @Override\n    public void enterPz(StrfTimeParser.PzContext ctx) {\n        // %z   The +hhmm or -hhmm numeric timezone.\n        builder.appendOffset(\"+HHMM\", \"+0000\");\n        zoneWasSpecified = true;\n    }\n\n    @Override\n    public void enterPZ(StrfTimeParser.PZContext ctx) {\n        // %Z   The timezone name or abbreviation.\n        builder.appendZoneText(TextStyle.SHORT);\n        zoneWasSpecified = true;\n    }\n\n    @Override\n    public void enterPplus(StrfTimeParser.PplusContext ctx) {\n        throw new UnsupportedStrfField(\"%p   The date and time in date(1) format.\");\n    }\n\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/main/java/nl/basjes/parse/httpdlog/dissectors/TimeStampDissector.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.httpdlog.dissectors;\n\nimport nl.basjes.parse.core.Casts;\nimport nl.basjes.parse.core.Dissector;\nimport nl.basjes.parse.core.Parsable;\nimport nl.basjes.parse.core.ParsedField;\nimport nl.basjes.parse.core.exceptions.DissectionFailure;\n\nimport java.text.DateFormatSymbols;\nimport java.time.LocalDateTime;\nimport java.time.ZoneOffset;\nimport java.time.ZonedDateTime;\nimport java.time.format.DateTimeFormatter;\nimport java.time.format.DateTimeFormatterBuilder;\nimport java.time.format.DateTimeParseException;\nimport java.time.format.TextStyle;\nimport java.time.temporal.WeekFields;\nimport java.util.ArrayList;\nimport java.util.EnumSet;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.regex.Pattern;\n\nimport static nl.basjes.parse.core.Casts.NO_CASTS;\nimport static nl.basjes.parse.core.Casts.STRING_ONLY;\nimport static nl.basjes.parse.core.Casts.STRING_OR_LONG;\n\npublic class TimeStampDissector extends Dissector {\n\n    // The default parser to what we find in the Apache httpd Logfiles\n    //                                                            [05/Sep/2010:11:27:50 +0200]\n    public static final String DEFAULT_APACHE_DATE_TIME_PATTERN = \"dd/MMM/yyyy:HH:mm:ss ZZ\";\n\n    // --------------------------------------------\n\n    private transient DateTimeFormatter formatter;\n    private String dateTimePattern;\n    private Locale locale;\n\n    // The month abbreviations this locale supports, used in attempts in recovering parse errors.\n    private String[] localeMonths;\n\n    @SuppressWarnings(\"UnusedDeclaration\")\n    public TimeStampDissector() {\n        this(DEFAULT_APACHE_DATE_TIME_PATTERN);\n    }\n\n    public TimeStampDissector(String newDateTimePattern) {\n        this(\"TIME.STAMP\", newDateTimePattern);\n    }\n\n    public TimeStampDissector(String inputType, String newDateTimePattern) {\n        setInputType(inputType);\n        if (newDateTimePattern == null ||\n            newDateTimePattern.trim().isEmpty()) {\n            setDateTimePattern(DEFAULT_APACHE_DATE_TIME_PATTERN);\n        } else {\n            setDateTimePattern(newDateTimePattern);\n        }\n        setLocale(Locale.UK); // The default Locale that follows the ISO-8601 WeekFields and has sensible names.\n    }\n\n    public TimeStampDissector setLocale(Locale newLocale) {\n        locale = newLocale;\n        localeMonths = new DateFormatSymbols(locale).getShortMonths();\n        return this;\n    }\n\n    public Locale getLocale() {\n        return locale;\n    }\n// --------------------------------------------\n\n    @Override\n    public boolean initializeFromSettingsParameter(String settings) {\n        // There is only one setting for this dissector\n        setDateTimePattern(settings);\n        return true; // Everything went right.\n    }\n\n    // --------------------------------------------\n\n    public void setDateTimePattern(String nDateTimePattern) {\n        this.dateTimePattern = nDateTimePattern;\n    }\n\n    protected void setFormatter(DateTimeFormatter newFormatter) {\n        formatter = newFormatter;\n    }\n\n    protected DateTimeFormatter getFormatter() {\n        if (formatter == null) {\n            formatter = new DateTimeFormatterBuilder()\n                .parseCaseInsensitive()\n                .appendPattern(dateTimePattern)\n                .toFormatter()\n                .withLocale(locale);\n        }\n        return formatter;\n    }\n\n    @Override\n    protected void initializeNewInstance(Dissector newInstance) {\n        TimeStampDissector newTimeStampDissector = (TimeStampDissector) newInstance;\n        newTimeStampDissector.setInputType(inputType);\n        newTimeStampDissector.setDateTimePattern(dateTimePattern);\n        newTimeStampDissector.setLocale(locale);\n    }\n\n    // --------------------------------------------\n\n    private String inputType = \"TIME.STAMP\";\n\n    @Override\n    public String getInputType() {\n        return inputType;\n    }\n\n    @Override\n    public final void setInputType(String nInputType) {\n        inputType = nInputType;\n    }\n\n    // --------------------------------------------\n\n    @Override\n    public List<String> getPossibleOutput() {\n        List<String> result = new ArrayList<>();\n        // As parsed\n        result.add(\"TIME.DAY:day\");\n        result.add(\"TIME.MONTHNAME:monthname\");\n        result.add(\"TIME.MONTH:month\");\n        result.add(\"TIME.WEEK:weekofweekyear\");\n        result.add(\"TIME.YEAR:weekyear\");\n        result.add(\"TIME.YEAR:year\");\n        result.add(\"TIME.HOUR:hour\");\n        result.add(\"TIME.MINUTE:minute\");\n        result.add(\"TIME.SECOND:second\");\n        result.add(\"TIME.MILLISECOND:millisecond\");\n        result.add(\"TIME.MICROSECOND:microsecond\");\n        result.add(\"TIME.NANOSECOND:nanosecond\");\n\n        result.add(\"TIME.DATE:date\"); // yyyy-MM-dd\n        result.add(\"TIME.TIME:time\"); // HH:mm:ss\n\n        // Timezone independent\n        result.add(\"TIME.ZONE:timezone\");\n        result.add(\"TIME.EPOCH:epoch\");\n\n        // In UTC timezone\n        result.add(\"TIME.DAY:day_utc\");\n        result.add(\"TIME.MONTHNAME:monthname_utc\");\n        result.add(\"TIME.MONTH:month_utc\");\n        result.add(\"TIME.WEEK:weekofweekyear_utc\");\n        result.add(\"TIME.YEAR:weekyear_utc\");\n        result.add(\"TIME.YEAR:year_utc\");\n        result.add(\"TIME.HOUR:hour_utc\");\n        result.add(\"TIME.MINUTE:minute_utc\");\n        result.add(\"TIME.SECOND:second_utc\");\n        result.add(\"TIME.MILLISECOND:millisecond_utc\");\n        result.add(\"TIME.MICROSECOND:microsecond_utc\");\n        result.add(\"TIME.NANOSECOND:nanosecond_utc\");\n\n        result.add(\"TIME.DATE:date_utc\"); // yyyy-MM-dd\n        result.add(\"TIME.TIME:time_utc\"); // HH:mm:ss\n\n        return result;\n    }\n\n    // --------------------------------------------\n\n    private boolean wantAnyAsParsed       = false;\n    private boolean wantAnyUTC            = false;\n    private boolean wantAnyTZIndependent  = false;\n\n    // As parsed\n    private boolean wantDay               = false;\n    private boolean wantMonthname         = false;\n    private boolean wantMonth             = false;\n    private boolean wantWeekOfWeekYear    = false;\n    private boolean wantWeekYear          = false;\n    private boolean wantYear              = false;\n    private boolean wantHour              = false;\n    private boolean wantMinute            = false;\n    private boolean wantSecond            = false;\n    private boolean wantMillisecond       = false;\n    private boolean wantMicrosecond       = false;\n    private boolean wantNanosecond        = false;\n    private boolean wantDate              = false;\n    private boolean wantTime              = false;\n\n\n    // Timezone independent\n    private boolean wantTimezone          = false;\n    private boolean wantEpoch             = false;\n\n    // In UTC timezone\n    private boolean wantDayUTC            = false;\n    private boolean wantMonthnameUTC      = false;\n    private boolean wantMonthUTC          = false;\n    private boolean wantWeekOfWeekYearUTC = false;\n    private boolean wantWeekYearUTC       = false;\n    private boolean wantYearUTC           = false;\n    private boolean wantHourUTC           = false;\n    private boolean wantMinuteUTC         = false;\n    private boolean wantSecondUTC         = false;\n    private boolean wantMillisecondUTC    = false;\n    private boolean wantMicrosecondUTC    = false;\n    private boolean wantNanosecondUTC     = false;\n    private boolean wantDateUTC           = false;\n    private boolean wantTimeUTC           = false;\n\n    @Override\n    public EnumSet<Casts> prepareForDissect(final String inputname, final String outputname) {\n        String name = extractFieldName(inputname, outputname);\n        switch (name) {\n            // As parsed\n            case \"day\":\n                wantDay = true;\n                return STRING_OR_LONG;\n\n            case \"monthname\":\n                wantMonthname = true;\n                return STRING_ONLY;\n\n            case \"month\":\n                wantMonth = true;\n                return STRING_OR_LONG;\n\n            case \"weekofweekyear\":\n                wantWeekOfWeekYear = true;\n                return STRING_OR_LONG;\n\n            case \"weekyear\":\n                wantWeekYear = true;\n                return STRING_OR_LONG;\n\n            case \"year\":\n                wantYear = true;\n                return STRING_OR_LONG;\n\n            case \"hour\":\n                wantHour = true;\n                return STRING_OR_LONG;\n\n            case \"minute\":\n                wantMinute = true;\n                return STRING_OR_LONG;\n\n            case \"second\":\n                wantSecond = true;\n                return STRING_OR_LONG;\n\n            case \"millisecond\":\n                wantMillisecond = true;\n                return STRING_OR_LONG;\n\n            case \"microsecond\":\n                wantMicrosecond = true;\n                return STRING_OR_LONG;\n\n            case \"nanosecond\":\n                wantNanosecond = true;\n                return STRING_OR_LONG;\n\n            case \"date\":\n                wantDate = true;\n                return STRING_ONLY;\n\n            case \"time\":\n                wantTime = true;\n                return STRING_ONLY;\n\n            // Timezone independent\n            case \"timezone\":\n                wantTimezone = true;\n                return STRING_ONLY;\n\n            case \"epoch\":\n                wantEpoch = true;\n                return STRING_OR_LONG;\n\n            // In UTC timezone\n            case \"day_utc\":\n                wantDayUTC = true;\n                return STRING_OR_LONG;\n\n            case \"monthname_utc\":\n                wantMonthnameUTC = true;\n                return STRING_ONLY;\n\n            case \"month_utc\":\n                wantMonthUTC = true;\n                return STRING_OR_LONG;\n\n            case \"weekofweekyear_utc\":\n                wantWeekOfWeekYearUTC = true;\n                return STRING_OR_LONG;\n\n            case \"weekyear_utc\":\n                wantWeekYearUTC = true;\n                return STRING_OR_LONG;\n\n            case \"year_utc\":\n                wantYearUTC = true;\n                return STRING_OR_LONG;\n\n            case \"hour_utc\":\n                wantHourUTC = true;\n                return STRING_OR_LONG;\n\n            case \"minute_utc\":\n                wantMinuteUTC = true;\n                return STRING_OR_LONG;\n\n            case \"second_utc\":\n                wantSecondUTC = true;\n                return STRING_OR_LONG;\n\n            case \"millisecond_utc\":\n                wantMillisecondUTC = true;\n                return STRING_OR_LONG;\n\n            case \"microsecond_utc\":\n                wantMicrosecondUTC = true;\n                return STRING_OR_LONG;\n\n            case \"nanosecond_utc\":\n                wantNanosecondUTC = true;\n                return STRING_OR_LONG;\n\n            case \"date_utc\":\n                wantDateUTC = true;\n                return STRING_ONLY;\n\n            case \"time_utc\":\n                wantTimeUTC = true;\n                return STRING_ONLY;\n\n            default:\n                return NO_CASTS;\n        }\n    }\n\n    // --------------------------------------------\n\n    @Override\n    public void prepareForRun() {\n        // As parsed\n        wantAnyAsParsed =\n               wantDay\n            || wantMonthname\n            || wantMonth\n            || wantWeekOfWeekYear\n            || wantWeekYear\n            || wantYear\n            || wantHour\n            || wantMinute\n            || wantSecond\n            || wantMillisecond\n            || wantMicrosecond\n            || wantNanosecond\n            || wantDate\n            || wantTime;\n\n        // Timezone independent\n        wantAnyTZIndependent =\n               wantTimezone\n            || wantEpoch;\n\n        // In UTC timezone\n        wantAnyUTC =\n               wantDayUTC\n            || wantMonthnameUTC\n            || wantMonthUTC\n            || wantWeekOfWeekYearUTC\n            || wantWeekYearUTC\n            || wantYearUTC\n            || wantHourUTC\n            || wantMinuteUTC\n            || wantSecondUTC\n            || wantMillisecondUTC\n            || wantMicrosecondUTC\n            || wantNanosecondUTC\n            || wantDateUTC\n            || wantTimeUTC;\n    }\n\n    // --------------------------------------------\n\n    private static final DateTimeFormatter ISO_DATE_FORMATTER = DateTimeFormatter.ofPattern(\"yyyy-MM-dd\");\n    private static final DateTimeFormatter ISO_TIME_FORMATTER = DateTimeFormatter.ofPattern(\"HH:mm:ss\");\n\n    @Override\n    public void dissect(final Parsable<?> parsable, final String inputname) throws DissectionFailure {\n        final ParsedField field = parsable.getParsableField(getInputType(), inputname);\n        dissect(field, parsable, inputname);\n    }\n\n    private ZonedDateTime parse(String fieldValue) throws DateTimeParseException {\n        ZonedDateTime dateTime;\n        try {\n            dateTime = getFormatter().parse(fieldValue, ZonedDateTime::from);\n        } catch (DateTimeParseException dtpe) {\n            // Some parse errors can be fixed.\n            fieldValue = attemptRecoverParseError(fieldValue, dtpe);\n            // Retry parsing with an adjusted fieldValue.\n            dateTime = getFormatter().parse(fieldValue, ZonedDateTime::from);\n        }\n        return dateTime;\n    }\n\n    protected void dissect(ParsedField field, final Parsable<?> parsable, final String inputname) throws DissectionFailure {\n        String fieldValue = field.getValue().getString();\n        if (fieldValue == null || fieldValue.isEmpty()) {\n            return; // Nothing to do here\n        }\n\n        ZonedDateTime dateTime;\n        try {\n            dateTime = parse(fieldValue);\n        } catch (DateTimeParseException dtpe) {\n            // If this fails it is not a line specific problem but a configuration problem.\n            throw new DissectionFailure(dtpe.getMessage()+\n                \"\\n          10        20        30        40        50        60        70        80        90        100       110       120\" +\n                \"\\n_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_\" +\n                \"\\n\"+fieldValue+\"\\n\\n\"+formatter.toString(), dtpe);\n        }\n\n        if (wantAnyTZIndependent) {\n            // Timezone independent\n            if (wantTimezone) {\n                parsable.addDissection(inputname, \"TIME.ZONE\", \"timezone\",\n                    dateTime.getZone().getDisplayName(TextStyle.FULL, locale));\n            }\n            if (wantEpoch) {\n                parsable.addDissection(inputname, \"TIME.EPOCH\", \"epoch\",\n                    dateTime.toInstant().toEpochMilli());\n            }\n        }\n\n        if (wantAnyAsParsed) {\n            LocalDateTime localDateTime = dateTime.toLocalDateTime();\n            // As parsed\n            if (wantDay) {\n                parsable.addDissection(inputname, \"TIME.DAY\", \"day\",\n                    localDateTime.getDayOfMonth());\n            }\n            if (wantMonthname) {\n                parsable.addDissection(inputname, \"TIME.MONTHNAME\", \"monthname\",\n                    localDateTime.getMonth().getDisplayName(TextStyle.FULL, locale));\n            }\n            if (wantMonth) {\n                parsable.addDissection(inputname, \"TIME.MONTH\", \"month\",\n                    localDateTime.getMonth().getValue());\n            }\n            if (wantWeekOfWeekYear) {\n                parsable.addDissection(inputname, \"TIME.WEEK\", \"weekofweekyear\",\n                    localDateTime.get(WeekFields.of(locale).weekOfWeekBasedYear()));\n            }\n            if (wantWeekYear) {\n                parsable.addDissection(inputname, \"TIME.YEAR\", \"weekyear\",\n                    localDateTime.get(WeekFields.of(locale).weekBasedYear()));\n            }\n            if (wantYear) {\n                parsable.addDissection(inputname, \"TIME.YEAR\", \"year\",\n                    localDateTime.getYear());\n            }\n            if (wantHour) {\n                parsable.addDissection(inputname, \"TIME.HOUR\", \"hour\",\n                    localDateTime.getHour());\n            }\n            if (wantMinute) {\n                parsable.addDissection(inputname, \"TIME.MINUTE\", \"minute\",\n                    localDateTime.getMinute());\n            }\n            if (wantSecond) {\n                parsable.addDissection(inputname, \"TIME.SECOND\", \"second\",\n                    localDateTime.getSecond());\n            }\n            if (wantMillisecond) {\n                parsable.addDissection(inputname, \"TIME.MILLISECOND\", \"millisecond\",\n                    localDateTime.getNano() / 1000000L);\n            }\n            if (wantMicrosecond) {\n                parsable.addDissection(inputname, \"TIME.MICROSECOND\", \"microsecond\",\n                    localDateTime.getNano() / 1000L);\n            }\n            if (wantNanosecond) {\n                parsable.addDissection(inputname, \"TIME.NANOSECOND\", \"nanosecond\",\n                    localDateTime.getNano());\n            }\n            if (wantDate) {\n                parsable.addDissection(inputname, \"TIME.DATE\", \"date\",\n                    localDateTime.format(ISO_DATE_FORMATTER));\n            }\n\n            if (wantTime) {\n                parsable.addDissection(inputname, \"TIME.TIME\", \"time\",\n                    localDateTime.format(ISO_TIME_FORMATTER));\n            }\n\n        }\n\n        if (wantAnyUTC) {\n            // In UTC timezone\n            ZonedDateTime zonedDateTime = dateTime.withZoneSameInstant(ZoneOffset.UTC);\n\n            if (wantDayUTC) {\n                parsable.addDissection(inputname, \"TIME.DAY\", \"day_utc\",\n                    zonedDateTime.getDayOfMonth());\n            }\n            if (wantMonthnameUTC) {\n                parsable.addDissection(inputname, \"TIME.MONTHNAME\", \"monthname_utc\",\n                    zonedDateTime.getMonth().getDisplayName(TextStyle.FULL, locale));\n            }\n            if (wantMonthUTC) {\n                parsable.addDissection(inputname, \"TIME.MONTH\", \"month_utc\",\n                    zonedDateTime.getMonthValue());\n            }\n            if (wantWeekOfWeekYearUTC) {\n                parsable.addDissection(inputname, \"TIME.WEEK\", \"weekofweekyear_utc\",\n                    zonedDateTime.get(WeekFields.ISO.weekOfWeekBasedYear()));\n            }\n            if (wantWeekYearUTC) {\n                parsable.addDissection(inputname, \"TIME.YEAR\", \"weekyear_utc\",\n                    zonedDateTime.get(WeekFields.ISO.weekBasedYear()));\n            }\n            if (wantYearUTC) {\n                parsable.addDissection(inputname, \"TIME.YEAR\", \"year_utc\",\n                    zonedDateTime.getYear());\n            }\n            if (wantHourUTC) {\n                parsable.addDissection(inputname, \"TIME.HOUR\", \"hour_utc\",\n                    zonedDateTime.getHour());\n            }\n            if (wantMinuteUTC) {\n                parsable.addDissection(inputname, \"TIME.MINUTE\", \"minute_utc\",\n                    zonedDateTime.getMinute());\n            }\n            if (wantSecondUTC) {\n                parsable.addDissection(inputname, \"TIME.SECOND\", \"second_utc\",\n                    zonedDateTime.getSecond());\n            }\n            if (wantMillisecondUTC) {\n                parsable.addDissection(inputname, \"TIME.MILLISECOND\", \"millisecond_utc\",\n                    zonedDateTime.getNano() / 1000000L);\n            }\n            if (wantMicrosecondUTC) {\n                parsable.addDissection(inputname, \"TIME.MICROSECOND\", \"microsecond_utc\",\n                    zonedDateTime.getNano() / 1000L);\n            }\n            if (wantNanosecondUTC) {\n                parsable.addDissection(inputname, \"TIME.NANOSECOND\", \"nanosecond_utc\",\n                    zonedDateTime.getNano());\n            }\n            if (wantDateUTC) {\n                parsable.addDissection(inputname, \"TIME.DATE\", \"date_utc\",\n                    zonedDateTime.format(ISO_DATE_FORMATTER));\n            }\n\n            if (wantTimeUTC) {\n                parsable.addDissection(inputname, \"TIME.TIME\", \"time_utc\",\n                    zonedDateTime.format(ISO_TIME_FORMATTER));\n            }\n\n        }\n    }\n\n    // --------------------------------------------\n\n    // Get the month abbreviations in (American) \"English\" as commonly used in logging.\n    final String[] englishMonths = new String[] {\n        \"jan\",\n        \"feb\",\n        \"mar\",\n        \"apr\",\n        \"may\",\n        \"jun\",\n        \"jul\",\n        \"aug\",\n        \"sep\",\n        \"oct\",\n        \"nov\",\n        \"dec\",\n    };\n\n    // Performance optimization\n    final String[] pqEnglishMonths = new String[] {\n        Pattern.quote(\"jan\"),\n        Pattern.quote(\"feb\"),\n        Pattern.quote(\"mar\"),\n        Pattern.quote(\"apr\"),\n        Pattern.quote(\"may\"),\n        Pattern.quote(\"jun\"),\n        Pattern.quote(\"jul\"),\n        Pattern.quote(\"aug\"),\n        Pattern.quote(\"sep\"),\n        Pattern.quote(\"oct\"),\n        Pattern.quote(\"nov\"),\n        Pattern.quote(\"dec\"),\n    };\n\n    // In some cases we have these specials\n    final String[] englishMonths4 = new String[] {\n        null,\n        null,\n        null,\n        null,\n        null,\n        \"june\",\n        \"july\",\n        null,\n        \"sept\",\n        null,\n        null,\n        null,\n        null,\n    };\n\n    // Performance optimization\n    final String[] pqEnglishMonths4 = new String[] {\n        null,\n        null,\n        null,\n        null,\n        null,\n        Pattern.quote(\"june\"),\n        Pattern.quote(\"july\"),\n        null,\n        Pattern.quote(\"sept\"),\n        null,\n        null,\n        null,\n        null,\n    };\n\n    private String attemptRecoverParseError(String fieldValue, DateTimeParseException dtpe) {\n        // Error handling: We may have the wrong name for the month here.\n        // Unicode CLDR has for different locales different \"short\" names\n        // for some months: Jun/June, Jul/July and Sep/Sept.\n        // At this point there is no parser I have that can handle these variations\n        // transparently, so in case of an exception: Try to apply a fix and redo.\n        // See\n        //   https://stackoverflow.com/questions/70928852/customize-a-locale-in-java/\n        //   https://unicode-org.atlassian.net/browse/CLDR-15317\n\n        fieldValue = fieldValue.toLowerCase(Locale.ROOT);\n\n        String updatedFieldValue = fieldValue;\n\n        // Fix idea: Convert some of the \"well known\" ways the Months names appear\n        //           into what the current Locale expects.\n        // FIXME: This does NOT fix all cases where this goes wrong.\n        for (int i = 0; i < englishMonths.length; i++) {\n            String value = englishMonths4[i];\n            if (value != null && updatedFieldValue.contains(value)) {\n                updatedFieldValue = updatedFieldValue\n                    .replaceAll(pqEnglishMonths4[i], localeMonths[i]);\n                continue;\n            }\n            value = englishMonths[i];\n            if (value != null && updatedFieldValue.contains(value)) {\n                updatedFieldValue = updatedFieldValue\n                    .replaceAll(pqEnglishMonths[i], localeMonths[i]);\n            }\n        }\n\n\n        if (fieldValue.equals(updatedFieldValue)) {\n            // No changes were actually applied: Apparently this was something different.\n            throw dtpe;\n        }\n        return updatedFieldValue;\n    }\n\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/main/java/nl/basjes/parse/httpdlog/dissectors/geoip/AbstractGeoIPDissector.java",
    "content": "/*\n * Apache HTTPD logparsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.httpdlog.dissectors.geoip;\n\nimport com.maxmind.db.CHMCache;\nimport com.maxmind.db.Reader;\nimport com.maxmind.geoip2.DatabaseReader;\nimport nl.basjes.parse.core.Dissector;\nimport nl.basjes.parse.core.Parsable;\nimport nl.basjes.parse.core.ParsedField;\nimport nl.basjes.parse.core.exceptions.DissectionFailure;\nimport nl.basjes.parse.core.exceptions.InvalidDissectorException;\n\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.net.InetAddress;\nimport java.net.UnknownHostException;\n\npublic abstract class AbstractGeoIPDissector extends Dissector {\n\n    static final String INPUT_TYPE = \"IP\";\n\n    String databaseFileName;\n\n    public AbstractGeoIPDissector() {\n    }\n\n    public AbstractGeoIPDissector(String databaseFileName) {\n        this.databaseFileName = databaseFileName;\n    }\n\n    @Override\n    public String getInputType() {\n        return INPUT_TYPE;\n    }\n\n    // --------------------------------------------\n\n    @Override\n    public boolean initializeFromSettingsParameter(String settings) {\n        databaseFileName = settings;\n        return true; // Everything went right.\n    }\n\n    // --------------------------------------------\n\n    @Override\n    protected void initializeNewInstance(Dissector newInstance) {\n        newInstance.initializeFromSettingsParameter(databaseFileName);\n    }\n\n    // --------------------------------------------\n\n    protected DatabaseReader reader;\n\n    @Override\n    public void prepareForRun() throws InvalidDissectorException {\n        // This creates the DatabaseReader object, which should be reused across lookups.\n        try {\n            reader = new DatabaseReader\n                .Builder(openDatabaseFile(databaseFileName))\n                .fileMode(Reader.FileMode.MEMORY)\n                .withCache(new CHMCache())\n                .build();\n        } catch (IOException e) {\n            throw new InvalidDissectorException(this.getClass().getCanonicalName() + \":\" + e.getMessage());\n        }\n    }\n\n    protected InputStream openDatabaseFile(String filename) throws FileNotFoundException {\n        return new FileInputStream(filename);\n    }\n\n\n    // --------------------------------------------\n\n    @Override\n    public void dissect(final Parsable<?> parsable, final String inputname) throws DissectionFailure {\n        final ParsedField field = parsable.getParsableField(INPUT_TYPE, inputname);\n\n        String fieldValue = field.getValue().getString();\n        if (fieldValue == null || fieldValue.isEmpty()) {\n            return; // Nothing to do here\n        }\n\n        InetAddress ipAddress;\n        try {\n            ipAddress = InetAddress.getByName(fieldValue);\n        } catch (UnknownHostException e) {\n            return;\n        }\n\n        dissect(parsable, inputname, ipAddress);\n    }\n\n    // --------------------------------------------\n\n    abstract void dissect(Parsable<?> parsable, String inputname, InetAddress ipAddress) throws DissectionFailure;\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/main/java/nl/basjes/parse/httpdlog/dissectors/geoip/GeoIPASNDissector.java",
    "content": "/*\n * Apache HTTPD logparsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.httpdlog.dissectors.geoip;\n\nimport com.maxmind.geoip2.exception.GeoIp2Exception;\nimport com.maxmind.geoip2.model.AsnResponse;\nimport com.maxmind.geoip2.model.IspResponse;\nimport nl.basjes.parse.core.Casts;\nimport nl.basjes.parse.core.Parsable;\nimport nl.basjes.parse.core.exceptions.DissectionFailure;\n\nimport java.io.IOException;\nimport java.net.InetAddress;\nimport java.util.ArrayList;\nimport java.util.EnumSet;\nimport java.util.List;\n\nimport static nl.basjes.parse.core.Casts.NO_CASTS;\nimport static nl.basjes.parse.core.Casts.STRING_ONLY;\nimport static nl.basjes.parse.core.Casts.STRING_OR_LONG;\n\npublic class GeoIPASNDissector extends AbstractGeoIPDissector {\n\n    @SuppressWarnings(\"unused\") // Used via reflection\n    public GeoIPASNDissector() {\n        super();\n    }\n\n    public GeoIPASNDissector(String databaseFileName) {\n        super(databaseFileName);\n    }\n\n    @Override\n    public List<String> getPossibleOutput() {\n        List<String> result = new ArrayList<>();\n\n        result.add(\"ASN:asn.number\");\n        result.add(\"STRING:asn.organization\");\n\n        return result;\n    }\n\n    private boolean wantAsnNumber = false;\n    private boolean wantAsnOrganization = false;\n\n    @Override\n    public EnumSet<Casts> prepareForDissect(final String inputname, final String outputname) {\n        String name = extractFieldName(inputname, outputname);\n\n        switch (name) {\n            case \"asn.number\":\n                wantAsnNumber = true;\n                return STRING_OR_LONG;\n\n            case \"asn.organization\":\n                wantAsnOrganization = true;\n                return STRING_ONLY;\n\n            default:\n                return NO_CASTS;\n        }\n    }\n\n    // --------------------------------------------\n\n    public void dissect(final Parsable<?> parsable, final String inputname, final InetAddress ipAddress) throws DissectionFailure {\n        AsnResponse response;\n        try {\n            response = reader.asn(ipAddress);\n        } catch (IOException | GeoIp2Exception e) {\n            return;\n        }\n\n        extractAsnFields(parsable, inputname, response);\n    }\n\n    protected void extractAsnFields(final Parsable<?> parsable, final String inputname, AsnResponse response) throws DissectionFailure {\n        if (wantAsnNumber) {\n            parsable.addDissection(inputname, \"ASN\", \"asn.number\", response.autonomousSystemNumber());\n        }\n        if (wantAsnOrganization) {\n            parsable.addDissection(inputname, \"STRING\", \"asn.organization\", response.autonomousSystemOrganization());\n        }\n    }\n\n    // The EXACT same function but now for an IspResponse ...\n    protected void extractAsnFields(final Parsable<?> parsable, final String inputname, IspResponse response) throws DissectionFailure {\n        if (wantAsnNumber) {\n            parsable.addDissection(inputname, \"ASN\", \"asn.number\", response.autonomousSystemNumber());\n        }\n        if (wantAsnOrganization) {\n            parsable.addDissection(inputname, \"STRING\", \"asn.organization\", response.autonomousSystemOrganization());\n        }\n    }\n    // --------------------------------------------\n\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/main/java/nl/basjes/parse/httpdlog/dissectors/geoip/GeoIPCityDissector.java",
    "content": "/*\n * Apache HTTPD logparsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.httpdlog.dissectors.geoip;\n\nimport com.maxmind.geoip2.exception.GeoIp2Exception;\nimport com.maxmind.geoip2.model.CityResponse;\nimport com.maxmind.geoip2.record.City;\nimport com.maxmind.geoip2.record.Location;\nimport com.maxmind.geoip2.record.Postal;\nimport com.maxmind.geoip2.record.Subdivision;\nimport nl.basjes.parse.core.Casts;\nimport nl.basjes.parse.core.Parsable;\nimport nl.basjes.parse.core.exceptions.DissectionFailure;\n\nimport java.io.IOException;\nimport java.net.InetAddress;\nimport java.util.EnumSet;\nimport java.util.List;\n\nimport static nl.basjes.parse.core.Casts.NO_CASTS;\nimport static nl.basjes.parse.core.Casts.STRING_ONLY;\nimport static nl.basjes.parse.core.Casts.STRING_OR_DOUBLE;\nimport static nl.basjes.parse.core.Casts.STRING_OR_LONG;\n\npublic class GeoIPCityDissector extends GeoIPCountryDissector {\n\n    @SuppressWarnings(\"unused\") // Used via reflection\n    public GeoIPCityDissector() {\n        super();\n    }\n\n    public GeoIPCityDissector(String databaseFileName) {\n        super(databaseFileName);\n    }\n\n    @Override\n    public List<String> getPossibleOutput() {\n        List<String> result = super.getPossibleOutput();\n\n        result.add(\"STRING:subdivision.name\");\n        result.add(\"STRING:subdivision.iso\");\n\n        result.add(\"STRING:city.name\");\n        result.add(\"NUMBER:city.confidence\");\n        result.add(\"NUMBER:city.geonameid\");\n\n        result.add(\"STRING:postal.code\");\n        result.add(\"NUMBER:postal.confidence\");\n\n        result.add(\"STRING:location.latitude\");\n        result.add(\"STRING:location.longitude\");\n        result.add(\"STRING:location.timezone\");\n        result.add(\"NUMBER:location.accuracyradius\");\n\n        return result;\n    }\n\n    private boolean wantSubdivisionName            = false;\n    private boolean wantSubdivisionIso             = false;\n    private boolean wantAnySubdivision             = false;\n\n    private boolean wantCityName                   = false;\n    private boolean wantCityConfidence             = false;\n    private boolean wantCityGeoNameId              = false;\n    private boolean wantAnyCity                    = false;\n\n    private boolean wantPostalCode                 = false;\n    private boolean wantPostalConfidence           = false;\n    private boolean wantAnyPostal                  = false;\n\n    private boolean wantLocationLatitude           = false;\n    private boolean wantLocationLongitude          = false;\n    private boolean wantLocationTimezone           = false;\n    private boolean wantLocationAccuracyRadius     = false;\n    private boolean wantAnyLocation                = false;\n\n\n    @Override\n    public EnumSet<Casts> prepareForDissect(final String inputname, final String outputname) {\n        EnumSet<Casts> result = super.prepareForDissect(inputname, outputname);\n        if (!result.isEmpty()) {\n            return result;\n        }\n\n        String name = extractFieldName(inputname, outputname);\n\n        switch (name) {\n\n            case \"subdivision.name\":\n                wantSubdivisionName = true;\n                wantAnySubdivision = true;\n                return STRING_ONLY;\n\n            case \"subdivision.iso\":\n                wantSubdivisionIso = true;\n                wantAnySubdivision = true;\n                return STRING_ONLY;\n\n            // ---------------------------------\n\n            case \"city.name\":\n                wantCityName = true;\n                wantAnyCity = true;\n                return STRING_ONLY;\n\n            case \"city.confidence\":\n                wantCityConfidence = true;\n                wantAnyCity = true;\n                return STRING_OR_LONG;\n\n            case \"city.geonameid\":\n                wantCityGeoNameId = true;\n                wantAnyCity = true;\n                return STRING_OR_LONG;\n\n            // ---------------------------------\n\n            case \"postal.code\":\n                wantPostalCode = true;\n                wantAnyPostal = true;\n                return STRING_ONLY;\n\n            case \"postal.confidence\":\n                wantPostalConfidence = true;\n                wantAnyPostal = true;\n                return STRING_OR_LONG;\n\n            // ---------------------------------\n\n            case \"location.latitude\":\n                wantLocationLatitude = true;\n                wantAnyLocation = true;\n                return STRING_OR_DOUBLE;\n\n            case \"location.longitude\":\n                wantLocationLongitude = true;\n                wantAnyLocation = true;\n                return STRING_OR_DOUBLE;\n\n            case \"location.accuracyradius\":\n                wantLocationAccuracyRadius = true;\n                wantAnyLocation = true;\n                return STRING_OR_LONG;\n\n            case \"location.timezone\":\n                wantLocationTimezone = true;\n                wantAnyLocation = true;\n                return STRING_ONLY;\n\n            default:\n                return NO_CASTS;\n        }\n    }\n\n    // --------------------------------------------\n\n    public void dissect(final Parsable<?> parsable, final String inputname, final InetAddress ipAddress) throws DissectionFailure {\n        // City is the 'Country' + more details.\n        CityResponse response;\n        try {\n            response = reader.city(ipAddress);\n        } catch (IOException | GeoIp2Exception e) {\n            return;\n        }\n\n        extractCityResponseFields(parsable, inputname, response);\n    }\n\n    protected void extractCityResponseFields(final Parsable<?> parsable, final String inputname, CityResponse response) throws DissectionFailure {\n        super.extractCityResponseFields(parsable, inputname, response);\n\n        if (wantAnySubdivision) {\n            extractSubdivisionFields(parsable, inputname, response.mostSpecificSubdivision());\n        }\n        if (wantAnyCity) {\n            extractCityFields(parsable, inputname, response.city());\n        }\n        if (wantAnyPostal) {\n            extractPostalFields(parsable, inputname, response.postal());\n        }\n        if (wantAnyLocation) {\n            extractLocationFields(parsable, inputname, response.location());\n        }\n    }\n\n    protected void extractSubdivisionFields(final Parsable<?> parsable, final String inputname, Subdivision subdivision) throws DissectionFailure {\n        if (subdivision != null) {\n            if (wantSubdivisionName) {\n                parsable.addDissection(inputname, \"STRING\", \"subdivision.name\", subdivision.name());\n            }\n            if (wantSubdivisionIso) {\n                parsable.addDissection(inputname, \"STRING\", \"subdivision.iso\", subdivision.isoCode());\n            }\n        }\n    }\n\n    protected void extractCityFields(final Parsable<?> parsable, final String inputname, City city) throws DissectionFailure {\n        if (city != null) {\n            if (wantCityName) {\n                parsable.addDissection(inputname, \"STRING\", \"city.name\", city.name());\n            }\n            if (wantCityConfidence) {\n                parsable.addDissection(inputname, \"NUMBER\", \"city.confidence\", city.confidence());\n            }\n            if (wantCityGeoNameId) {\n                parsable.addDissection(inputname, \"NUMBER\", \"city.geonameid\", city.geonameId());\n            }\n        }\n    }\n\n    protected void extractPostalFields(final Parsable<?> parsable, final String inputname, Postal postal) throws DissectionFailure {\n        if (postal != null) {\n            if (wantPostalCode) {\n                parsable.addDissection(inputname, \"STRING\", \"postal.code\", postal.code());\n            }\n            if (wantPostalConfidence) {\n                parsable.addDissection(inputname, \"NUMBER\", \"postal.confidence\", postal.confidence());\n            }\n        }\n    }\n\n    protected void extractLocationFields(final Parsable<?> parsable, final String inputname, Location location) throws DissectionFailure {\n        if (location != null) {\n            if (wantLocationLatitude) {\n                parsable.addDissection(inputname, \"STRING\", \"location.latitude\", location.latitude());\n            }\n            if (wantLocationLongitude) {\n                parsable.addDissection(inputname, \"STRING\", \"location.longitude\", location.longitude());\n            }\n            if (wantLocationTimezone) {\n                parsable.addDissection(inputname, \"STRING\", \"location.timezone\", location.timeZone());\n            }\n            if (wantLocationAccuracyRadius) {\n                parsable.addDissection(inputname, \"NUMBER\", \"location.accuracyradius\", location.accuracyRadius());\n            }\n        }\n    }\n\n    // --------------------------------------------\n\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/main/java/nl/basjes/parse/httpdlog/dissectors/geoip/GeoIPCountryDissector.java",
    "content": "/*\n * Apache HTTPD logparsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.httpdlog.dissectors.geoip;\n\nimport com.maxmind.geoip2.exception.GeoIp2Exception;\nimport com.maxmind.geoip2.model.CityResponse;\nimport com.maxmind.geoip2.model.CountryResponse;\nimport com.maxmind.geoip2.record.Continent;\nimport com.maxmind.geoip2.record.Country;\nimport nl.basjes.parse.core.Casts;\nimport nl.basjes.parse.core.Parsable;\nimport nl.basjes.parse.core.exceptions.DissectionFailure;\n\nimport java.io.IOException;\nimport java.net.InetAddress;\nimport java.util.ArrayList;\nimport java.util.EnumSet;\nimport java.util.List;\n\nimport static nl.basjes.parse.core.Casts.NO_CASTS;\nimport static nl.basjes.parse.core.Casts.STRING_ONLY;\nimport static nl.basjes.parse.core.Casts.STRING_OR_LONG;\n\npublic class GeoIPCountryDissector extends AbstractGeoIPDissector {\n\n    @SuppressWarnings(\"unused\") // Used via reflection\n    public GeoIPCountryDissector() {\n        super();\n    }\n\n    public GeoIPCountryDissector(String databaseFileName) {\n        super(databaseFileName);\n    }\n\n    @Override\n    public List<String> getPossibleOutput() {\n        List<String> result = new ArrayList<>();\n\n        result.add(\"STRING:continent.name\");\n        result.add(\"STRING:continent.code\");\n        result.add(\"STRING:country.name\");\n        result.add(\"STRING:country.iso\");\n        result.add(\"NUMBER:country.getconfidence\");\n        result.add(\"BOOLEAN:country.isineuropeanunion\");\n\n        return result;\n    }\n\n    private boolean wantContinentName               = false;\n    private boolean wantContinentCode               = false;\n    private boolean wantAnyContinent                = false;\n\n    private boolean wantCountryName                 = false;\n    private boolean wantCountryIso                  = false;\n    private boolean wantCountryGetConfidence        = false;\n    private boolean wantCountryIsInEuropeanUnion    = false;\n    private boolean wantAnyCountry                  = false;\n\n    @Override\n    public EnumSet<Casts> prepareForDissect(final String inputname, final String outputname) {\n        String name = extractFieldName(inputname, outputname);\n\n        switch (name) {\n            case \"continent.name\":\n                wantContinentName = true;\n                wantAnyContinent = true;\n                return STRING_ONLY;\n\n            case \"continent.code\":\n                wantContinentCode = true;\n                wantAnyContinent = true;\n                return STRING_ONLY;\n\n            case \"country.name\":\n                wantCountryName = true;\n                wantAnyCountry = true;\n                return STRING_ONLY;\n\n            case \"country.iso\":\n                wantCountryIso = true;\n                wantAnyCountry = true;\n                return STRING_ONLY;\n\n            case \"country.getconfidence\":\n                wantCountryGetConfidence = true;\n                wantAnyCountry = true;\n                return STRING_OR_LONG;\n\n            case \"country.isineuropeanunion\":\n                wantCountryIsInEuropeanUnion = true;\n                wantAnyCountry = true;\n                return STRING_OR_LONG;\n\n            default:\n                return NO_CASTS;\n        }\n    }\n\n    // --------------------------------------------\n\n    public void dissect(final Parsable<?> parsable, final String inputname, final InetAddress ipAddress)\n        throws DissectionFailure {\n        CountryResponse response;\n        try {\n            response = reader.country(ipAddress);\n        } catch (IOException | GeoIp2Exception e) {\n            return;\n        }\n        extractCountryResponseFields(parsable, inputname, response);\n    }\n\n    protected void extractCountryResponseFields(final Parsable<?> parsable, final String inputname, CountryResponse response)\n        throws DissectionFailure {\n        if (wantAnyContinent) {\n            extractContinentFields(parsable, inputname, response.continent());\n        }\n        if (wantAnyCountry) {\n            extractCountryFields(parsable, inputname, response.country());\n        }\n    }\n\n    // --------------------------------------------\n    // The EXACT same function but now for a CityResponse ...\n    protected void extractCityResponseFields(final Parsable<?> parsable, final String inputname, CityResponse response) throws DissectionFailure {\n        if (wantAnyContinent) {\n            extractContinentFields(parsable, inputname, response.continent());\n        }\n        if (wantAnyCountry) {\n            extractCountryFields(parsable, inputname, response.country());\n        }\n    }\n\n    protected void extractContinentFields(final Parsable<?> parsable, final String inputname, Continent continent)\n        throws DissectionFailure {\n        if (continent != null) {\n            if (wantContinentName) {\n                parsable.addDissection(inputname, \"STRING\", \"continent.name\", continent.name());\n            }\n            if (wantContinentCode) {\n                parsable.addDissection(inputname, \"STRING\", \"continent.code\", continent.code());\n            }\n        }\n    }\n\n    protected void extractCountryFields(final Parsable<?> parsable, final String inputname, Country country)\n        throws DissectionFailure {\n        if (country != null) {\n            if (wantCountryName) {\n                parsable.addDissection(inputname, \"STRING\", \"country.name\", country.name());\n            }\n            if (wantCountryIso) {\n                parsable.addDissection(inputname, \"STRING\", \"country.iso\", country.isoCode());\n            }\n\n            if (wantCountryGetConfidence) {\n                parsable.addDissection(inputname, \"NUMBER\", \"country.getconfidence\", country.confidence());\n            }\n            if (wantCountryIsInEuropeanUnion) {\n                parsable.addDissection(inputname, \"BOOLEAN\", \"country.isineuropeanunion\", country.isInEuropeanUnion() ? 1L : 0L);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/main/java/nl/basjes/parse/httpdlog/dissectors/geoip/GeoIPISPDissector.java",
    "content": "/*\n * Apache HTTPD logparsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.httpdlog.dissectors.geoip;\n\nimport com.maxmind.geoip2.exception.GeoIp2Exception;\nimport com.maxmind.geoip2.model.IspResponse;\nimport nl.basjes.parse.core.Casts;\nimport nl.basjes.parse.core.Parsable;\nimport nl.basjes.parse.core.exceptions.DissectionFailure;\n\nimport java.io.IOException;\nimport java.net.InetAddress;\nimport java.util.EnumSet;\nimport java.util.List;\n\nimport static nl.basjes.parse.core.Casts.NO_CASTS;\nimport static nl.basjes.parse.core.Casts.STRING_ONLY;\n\npublic class GeoIPISPDissector extends GeoIPASNDissector {\n\n    @SuppressWarnings(\"unused\") // Used via reflection\n    public GeoIPISPDissector() {\n        super();\n    }\n\n    public GeoIPISPDissector(String databaseFileName) {\n        super(databaseFileName);\n    }\n\n    @Override\n    public List<String> getPossibleOutput() {\n        List<String> result = super.getPossibleOutput();\n\n        result.add(\"STRING:isp.name\");\n        result.add(\"STRING:isp.organization\");\n\n        return result;\n    }\n\n    private boolean wantIspName = false;\n    private boolean wantIspOrganization = false;\n\n    @Override\n    public EnumSet<Casts> prepareForDissect(final String inputname, final String outputname) {\n        EnumSet<Casts> result = super.prepareForDissect(inputname, outputname);\n        if (!result.isEmpty()) {\n            return result;\n        }\n        String name = extractFieldName(inputname, outputname);\n\n        switch (name) {\n            case \"isp.name\":\n                wantIspName = true;\n                return STRING_ONLY;\n\n            case \"isp.organization\":\n                wantIspOrganization = true;\n                return STRING_ONLY;\n\n            default:\n                return NO_CASTS;\n        }\n    }\n\n    // --------------------------------------------\n\n    public void dissect(final Parsable<?> parsable, final String inputname, final InetAddress ipAddress) throws DissectionFailure {\n        IspResponse response;\n        try {\n            response = reader.isp(ipAddress);\n        } catch (IOException | GeoIp2Exception e) {\n            return;\n        }\n\n        extractAsnFields(parsable, inputname, response);\n        extractIspFields(parsable, inputname, response);\n    }\n\n    protected void extractIspFields(final Parsable<?> parsable, final String inputname, IspResponse response) throws DissectionFailure {\n        if (wantIspName) {\n            parsable.addDissection(inputname, \"STRING\", \"isp.name\", response.isp());\n        }\n        if (wantIspOrganization) {\n            parsable.addDissection(inputname, \"STRING\", \"isp.organization\", response.organization());\n        }\n    }\n\n\n    // --------------------------------------------\n\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/main/java/nl/basjes/parse/httpdlog/dissectors/nginxmodules/CoreLogModule.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage nl.basjes.parse.httpdlog.dissectors.nginxmodules;\n\nimport nl.basjes.parse.httpdlog.dissectors.tokenformat.NamedTokenParser;\nimport nl.basjes.parse.httpdlog.dissectors.tokenformat.TokenFormatDissector;\nimport nl.basjes.parse.httpdlog.dissectors.tokenformat.TokenParser;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport static nl.basjes.parse.core.Casts.STRING_ONLY;\nimport static nl.basjes.parse.core.Casts.STRING_OR_LONG;\nimport static nl.basjes.parse.httpdlog.dissectors.tokenformat.TokenParser.FORMAT_CLF_IP;\nimport static nl.basjes.parse.httpdlog.dissectors.tokenformat.TokenParser.FORMAT_CLF_NUMBER;\nimport static nl.basjes.parse.httpdlog.dissectors.tokenformat.TokenParser.FORMAT_HEXDIGIT;\nimport static nl.basjes.parse.httpdlog.dissectors.tokenformat.TokenParser.FORMAT_HEXNUMBER;\nimport static nl.basjes.parse.httpdlog.dissectors.tokenformat.TokenParser.FORMAT_NO_SPACE_STRING;\nimport static nl.basjes.parse.httpdlog.dissectors.tokenformat.TokenParser.FORMAT_NUMBER;\nimport static nl.basjes.parse.httpdlog.dissectors.tokenformat.TokenParser.FORMAT_NUMBER_DECIMAL;\nimport static nl.basjes.parse.httpdlog.dissectors.tokenformat.TokenParser.FORMAT_STANDARD_TIME_ISO8601;\nimport static nl.basjes.parse.httpdlog.dissectors.tokenformat.TokenParser.FORMAT_STANDARD_TIME_US;\nimport static nl.basjes.parse.httpdlog.dissectors.tokenformat.TokenParser.FORMAT_STRING;\n\n// Implement the variables described here:\n// https://nginx.org/en/docs/http/ngx_http_log_module.html#log_format\n// https://nginx.org/en/docs/http/ngx_http_core_module.html#variables\npublic class CoreLogModule implements NginxModule {\n    @Override\n    public List<TokenParser> getTokenParsers() {\n        List<TokenParser> parsers = new ArrayList<>(60);\n\n        // -------\n        // $bytes_sent\n        // number of bytes sent to a client (1.3.8, 1.2.5)\n        parsers.add(new TokenParser(\"$bytes_sent\",\n            \"response.bytes\", \"BYTES\",\n            STRING_OR_LONG, FORMAT_NUMBER));\n\n        // -------\n        // $bytes_received\n        // number of bytes received from a client (1.11.4)\n        parsers.add(new TokenParser(\"$bytes_received\",\n            \"request.bytes\", \"BYTES\",\n            STRING_OR_LONG, FORMAT_NUMBER));\n\n        // -------\n        // $connection\n        // connection serial number (1.3.8, 1.2.5)\n        parsers.add(new TokenParser(\"$connection\",\n            \"connection.serial_number\", \"NUMBER\",\n            STRING_OR_LONG, FORMAT_CLF_NUMBER, -1));\n\n        // -------\n        // $connection_requests\n        // current number of requests made through a connection (1.3.8, 1.2.5)\n        parsers.add(new TokenParser(\"$connection_requests\",\n            \"connection.requestnr\", \"NUMBER\",\n            STRING_OR_LONG, FORMAT_CLF_NUMBER));\n\n        // -------\n        // $msec\n        // time in seconds with a milliseconds resolution at the time of the log write\n        // Example value:  1483455396.639\n        parsers.add(new TokenParser(\"$msec\",\n            \"request.receive.time.epoch\", \"TIME.EPOCH_SECOND_MILLIS\",\n            STRING_ONLY, \"[0-9]+\\\\.[0-9][0-9][0-9]\"));\n\n        // -------\n        // $status\n        // response status\n        parsers.add(new TokenParser(\"$status\",\n            \"request.status.last\", \"STRING\",\n            STRING_ONLY, FORMAT_NO_SPACE_STRING));\n\n        // -------\n        // $time_iso8601\n        // local time in the ISO 8601 standard format (1.3.12, 1.2.7)\n        parsers.add(new TokenParser(\"$time_iso8601\",\n            \"request.receive.time\", \"TIME.ISO8601\",\n            STRING_ONLY, FORMAT_STANDARD_TIME_ISO8601));\n\n        // -------\n        // $time_local\n        // local time in the Common Log Format (1.3.12, 1.2.7)\n        parsers.add(new TokenParser(\"$time_local\",\n            \"request.receive.time\", \"TIME.STAMP\",\n            STRING_ONLY, FORMAT_STANDARD_TIME_US));\n\n        // https://nginx.org/en/docs/http/ngx_http_core_module.html#var_bytes_sent\n        // -------\n        // $arg_name\n        // argument name in the request line\n        parsers.add(new NamedTokenParser(\"\\\\$arg_([a-z0-9\\\\-\\\\_]*)\",\n            \"request.firstline.uri.query.\", \"STRING\",\n            STRING_ONLY, FORMAT_STRING));\n\n        // -------\n        // $is_args\n        // “?” if a request line has arguments, or an empty string otherwise\n        parsers.add(new TokenParser(\"$is_args\",\n            \"request.firstline.uri.is_args\", \"STRING\",\n            STRING_ONLY, FORMAT_STRING));\n\n        // -------\n        // $args\n        // arguments in the request line\n        parsers.add(new TokenParser(\"$args\",\n            \"request.firstline.uri.query\", \"HTTP.QUERYSTRING\",\n            STRING_ONLY, FORMAT_STRING));\n        // -------\n        // $query_string\n        // same as $args\n        parsers.add(new TokenParser(\"$query_string\",\n            \"request.firstline.uri.query\", \"HTTP.QUERYSTRING\",\n            STRING_ONLY, FORMAT_STRING));\n\n        // -------\n        // $body_bytes_sent\n        // number of bytes sent to a client, not counting the response header; this variable is compatible with\n        // the “%B” parameter of the mod_log_config Apache module\n        parsers.add(new TokenParser(\"$body_bytes_sent\",\n            \"response.body.bytes\", \"BYTES\",\n            STRING_OR_LONG, FORMAT_NUMBER));\n\n        // -------\n        // $content_length\n        // “Content-Length” request header field\n        parsers.add(new TokenParser(\"$content_length\",\n            \"request.header.content_length\", \"HTTP.HEADER\",\n            STRING_ONLY, FORMAT_STRING));\n\n        // -------\n        // $content_type\n        // “Content-Type” request header field\n        parsers.add(new TokenParser(\"$content_type\",\n            \"request.header.content_type\", \"HTTP.HEADER\",\n            STRING_ONLY, FORMAT_STRING));\n\n        // -------\n        // $cookie_name\n        // the name cookie\n        parsers.add(new NamedTokenParser(\"\\\\$cookie_([a-z0-9\\\\-_]*)\",\n            \"request.cookies.\", \"HTTP.COOKIE\",\n            STRING_ONLY, FORMAT_STRING));\n\n        // -------\n        // $document_root\n        // root or alias directive’s value for the current request\n        parsers.add(new TokenParser(\"$document_root\",\n            \"request.firstline.document_root\", \"STRING\",\n            STRING_ONLY, FORMAT_NO_SPACE_STRING));\n\n        // -------\n        // $realpath_root\n        // an absolute pathname corresponding to the root or alias directive’s value for the current request,\n        // with all symbolic links resolved to real paths\n        parsers.add(new TokenParser(\"$realpath_root\",\n            \"request.firstline.realpath_root\", \"STRING\",\n            STRING_ONLY, FORMAT_NO_SPACE_STRING));\n\n        // -------\n        // $host\n        // in this order of precedence: host name from the request line, or host name from the “Host” request header field,\n        // or the server name matching a request\n        parsers.add(new TokenParser(\"$host\",\n            \"connection.server.name\", \"STRING\",\n            STRING_ONLY, FORMAT_NO_SPACE_STRING, -1));\n\n        // -------\n        // $hostname\n        // host name\n        parsers.add(new TokenParser(\"$hostname\",\n            \"connection.client.host\", \"STRING\",\n            STRING_ONLY, FORMAT_NO_SPACE_STRING));\n\n        // -------\n        // $http_<name>\n        // arbitrary request header field; the last part of a variable name is the field name converted\n        // to lower case with dashes replaced by underscores\n        parsers.add(new NamedTokenParser(\"\\\\$http_([a-z0-9\\\\-_]*)\",\n            \"request.header.\", \"HTTP.HEADER\",\n            STRING_ONLY, FORMAT_STRING));\n\n        parsers.add(new TokenParser(\"$http_user_agent\",\n            \"request.user-agent\", \"HTTP.USERAGENT\",\n            STRING_ONLY, FORMAT_STRING, 1));\n\n        parsers.add(new TokenParser(\"$http_referer\",\n            \"request.referer\", \"HTTP.URI\",\n            STRING_ONLY, FORMAT_NO_SPACE_STRING, 1));\n\n        // -------\n        // $https\n        // “on” if connection operates in SSL mode, or an empty string otherwise\n        parsers.add(new TokenParser(\"$https\",\n            \"connection.https\", \"STRING\",\n            STRING_ONLY, FORMAT_NO_SPACE_STRING));\n\n        // -------\n        // $limit_rate\n        // setting this variable enables response rate limiting; see limit_rate\n        parsers.add(new TokenFormatDissector.NotImplementedTokenParser(\"$limit_rate\",\n            \"nginx_parameter_not_intended_for_logging\", FORMAT_NO_SPACE_STRING, 0));\n\n        // -------\n        // $nginx_version\n        // nginx version\n        parsers.add(new TokenParser(\"$nginx_version\",\n            \"server.nginx.version\", \"STRING\",\n            STRING_ONLY, TokenParser.FORMAT_STRING));\n\n        // -------\n        // $pid\n        // PID of the worker process\n        parsers.add(new TokenParser(\"$pid\",\n            \"connection.server.child.processid\", \"NUMBER\",\n            STRING_OR_LONG, FORMAT_NUMBER));\n\n        // -------\n        // $protocol\n        // protocol used to communicate with the client: TCP or UDP (1.11.4)\n        parsers.add(new TokenParser(\"$protocol\",\n            \"connection.protocol\", \"STRING\",\n            STRING_ONLY, FORMAT_NO_SPACE_STRING));\n\n        // -------\n        // $pipe\n        // “p” if request was pipelined, “.” otherwise (1.3.12, 1.2.7)\n        parsers.add(new TokenParser(\"$pipe\",\n            \"connection.nginx.pipe\", \"STRING\",\n            STRING_ONLY, \".\"));\n\n        // -------\n        // $proxy_protocol_addr\n        // client address from the PROXY protocol header, or an empty string otherwise (1.5.12)\n        // The PROXY protocol must be previously enabled by setting the proxy_protocol parameter\n        // in the listen directive.\n        parsers.add(new TokenParser(\"$proxy_protocol_addr\",\n            \"connection.client.proxy.host\", \"IP\",\n            STRING_OR_LONG, FORMAT_CLF_IP));\n\n        // $proxy_protocol_port\n        // client port from the PROXY protocol header, or an empty string otherwise (1.11.4)\n        parsers.add(new TokenParser(\"$proxy_protocol_port\",\n            \"connection.client.proxy.port\", \"PORT\",\n            STRING_OR_LONG, FORMAT_CLF_NUMBER));\n\n        // -------\n        // $remote_addr\n        // client address\n        parsers.add(new TokenParser(\"$remote_addr\",\n            \"connection.client.host\", \"IP\",\n            STRING_OR_LONG, FORMAT_CLF_IP));\n\n        // -------\n        // $binary_remote_addr\n        // client address in a binary form, value’s length is always 4 bytes\n        String formatHexByte = \"\\\\\\\\x\" + FORMAT_HEXDIGIT + FORMAT_HEXDIGIT;\n        parsers.add(new TokenParser(\"$binary_remote_addr\",\n            \"connection.client.host\", \"IP_BINARY\",\n            STRING_OR_LONG, formatHexByte + formatHexByte + formatHexByte + formatHexByte));\n\n        // -------\n        // $remote_port\n        // client port\n        parsers.add(new TokenParser(\"$remote_port\",\n            \"connection.client.port\", \"PORT\",\n            STRING_OR_LONG, FORMAT_NUMBER));\n\n        // -------\n        // $remote_user\n        // user name supplied with the Basic authentication\n        parsers.add(new TokenParser(\"$remote_user\",\n            \"connection.client.user\", \"STRING\",\n            STRING_ONLY, FORMAT_NO_SPACE_STRING));\n\n        // -------\n        // $request\n        // full original request line\n        parsers.add(new TokenParser(\"$request\",\n            \"request.firstline\", \"HTTP.FIRSTLINE\",\n            STRING_ONLY, FORMAT_STRING, -2));\n\n        // -------\n        // $request_body\n        // request body\n        // The variable’s value is made available in locations processed by the proxy_pass,\n        // fastcgi_pass, uwsgi_pass, and scgi_pass directives.\n        parsers.add(new TokenFormatDissector.NotImplementedTokenParser(\"$request_body\",\n            \"nginx_parameter_not_intended_for_logging\",\n            FORMAT_STRING, -1));\n\n        // -------\n        // $request_body_file\n        // name of a temporary file with the request body\n        // At the end of processing, the file needs to be removed. To always write the request body to a file,\n        // client_body_in_file_only needs to be enabled. When the name of a temporary file is passed in a proxied request\n        // or in a request to a FastCGI/uwsgi/SCGI server, passing the request body should be disabled by the\n        // proxy_pass_request_body off, fastcgi_pass_request_body off, uwsgi_pass_request_body off, or\n        // scgi_pass_request_body off directives, respectively.\n        parsers.add(new TokenFormatDissector.NotImplementedTokenParser(\"$request_body_file\",\n            \"nginx_parameter_not_intended_for_logging\",\n            FORMAT_STRING, -1));\n\n        // -------\n        // $request_completion\n        // “OK” if a request has completed, or an empty string otherwise\n        parsers.add(new TokenParser(\"$request_completion\",\n            \"request.completion\", \"STRING\",\n            STRING_ONLY, FORMAT_NO_SPACE_STRING));\n\n        // -------\n        // $request_filename\n        // file path for the current request, based on the root or alias directives, and the request URI\n        parsers.add(new TokenParser(\"$request_filename\",\n            \"server.filename\", \"FILENAME\",\n            STRING_ONLY, TokenParser.FORMAT_STRING));\n\n        // -------\n        // $request_length\n        // request length (including request line, header, and request body) (1.3.12, 1.2.7)\n        parsers.add(new TokenParser(\"$request_length\",\n            \"request.bytes\", \"BYTES\",\n            STRING_OR_LONG, TokenParser.FORMAT_CLF_NUMBER));\n\n        // -------\n        // $request_method\n        // request method, usually “GET” or “POST”\n        parsers.add(new TokenParser(\"$request_method\",\n            \"request.firstline.method\", \"HTTP.METHOD\",\n            STRING_ONLY, FORMAT_NO_SPACE_STRING));\n\n        // -------\n        // $request_time\n        // request processing time in seconds with a milliseconds resolution (1.3.9, 1.2.6);\n        // time elapsed since the first bytes were read from the client\n        parsers.add(new TokenParser(\"$request_time\",\n            \"response.server.processing.time\", \"SECOND_MILLIS\",\n            STRING_ONLY, FORMAT_NUMBER_DECIMAL));\n\n        // -------\n        // $request_uri\n        // full original request URI (with arguments)\n        parsers.add(new TokenParser(\"$request_uri\",\n            \"request.firstline.uri\", \"HTTP.URI\",\n            STRING_ONLY, FORMAT_NO_SPACE_STRING));\n\n        // -------\n        // $request_id\n        // unique request identifier generated from 16 random bytes, in hexadecimal (1.11.0)\n        parsers.add(new TokenParser(\"$request_id\",\n            \"request.id\", \"STRING\",\n            STRING_ONLY, FORMAT_HEXNUMBER));\n\n        // -------\n        // $uri\n        // current URI in request, normalized\n        // The value of $uri may change during request processing, e.g. when doing internal redirects, or when using index files.\n        parsers.add(new TokenParser(\"$uri\",\n            \"request.firstline.uri.normalized\", \"HTTP.URI\",\n            STRING_ONLY, FORMAT_STRING));\n        // -------\n        // $document_uri\n        // same as $uri\n        parsers.add(new TokenParser(\"$document_uri\",\n            \"request.firstline.uri.normalized\", \"HTTP.URI\",\n            STRING_ONLY, FORMAT_STRING));\n\n        // -------\n        // $scheme\n        // request scheme, “http” or “https”\n        parsers.add(new TokenParser(\"$scheme\",\n            \"request.firstline.uri.protocol\", \"HTTP.PROTOCOL\",\n            STRING_ONLY, FORMAT_NO_SPACE_STRING));\n\n        // -------\n        // $sent_http_name\n        // arbitrary response header field; the last part of a variable name is the field name converted to lower case with\n        // dashes replaced by underscores\n        parsers.add(new NamedTokenParser(\"\\\\$sent_http_([a-z0-9\\\\-_]*)\",\n            \"response.header.\", \"HTTP.HEADER\",\n            STRING_ONLY, FORMAT_STRING));\n\n        // -------\n        // $sent_trailer_name\n        // arbitrary field sent at the end of the response (1.13.2); the last part of a variable name is the field name\n        // converted to lower case with dashes replaced by underscores\n        parsers.add(new NamedTokenParser(\"\\\\$sent_trailer_([a-z0-9\\\\-_]*)\",\n            \"response.trailer.\", \"HTTP.TRAILER\",\n            STRING_ONLY, FORMAT_STRING));\n\n        // -------\n        // $server_addr\n        // an address of the server which accepted a request\n        // Computing a value of this variable usually requires one system call. To avoid a system call, the listen\n        // directives must specify addresses and use the bind parameter.\n        parsers.add(new TokenParser(\"$server_addr\",\n            \"connection.server.ip\", \"IP\",\n            STRING_OR_LONG, FORMAT_CLF_IP));\n\n        // -------\n        // $server_name\n        // name of the server which accepted a request\n        parsers.add(new TokenParser(\"$server_name\",\n            \"connection.server.name\", \"STRING\",\n            STRING_ONLY, FORMAT_NO_SPACE_STRING));\n\n        // -------\n        // $server_port\n        // port of the server which accepted a request\n        parsers.add(new TokenParser(\"$server_port\",\n            \"connection.server.port\", \"PORT\",\n            STRING_OR_LONG, FORMAT_NUMBER));\n\n        // -------\n        // $server_protocol\n        // request protocol, usually “HTTP/1.0” or “HTTP/1.1”\n        parsers.add(new TokenParser(\"$server_protocol\",\n            \"request.firstline.protocol\", \"HTTP.PROTOCOL_VERSION\",\n            STRING_OR_LONG, FORMAT_NO_SPACE_STRING));\n\n        // $session_time\n        // session duration in seconds with a milliseconds resolution (1.11.4);\n        parsers.add(new TokenParser(\"$session_time\",\n            \"connection.session.time\", \"SECOND_MILLIS\",\n            STRING_ONLY, FORMAT_NUMBER_DECIMAL));\n\n        // -------\n        // $tcpinfo_rtt, $tcpinfo_rttvar, $tcpinfo_snd_cwnd, $tcpinfo_rcv_space\n        // information about the client TCP connection; available on systems that support the TCP_INFO socket option\n        // See http://linuxgazette.net/136/pfeiffer.html\n        //      tcpi_rtt and tcpi_rttvar are the Round Trip Time (RTT), and its smoothed mean deviation maximum measured in microseconds\n        // $tcpinfo_rtt\n        // $tcpinfo_rttvar\n        parsers.add(new TokenParser(\"$tcpinfo_rtt\",\n            \"connection.tcpinfo.rtt\", \"MICROSECONDS\",\n            STRING_OR_LONG, FORMAT_NUMBER, -1));\n        parsers.add(new TokenParser(\"$tcpinfo_rttvar\",\n            \"connection.tcpinfo.rttvar\", \"MICROSECONDS\",\n            STRING_OR_LONG, FORMAT_NUMBER));\n\n        // $tcpinfo_snd_cwnd\n        //      tcpi_snd_cwnd is the sending congestion window.\n        parsers.add(new TokenParser(\"$tcpinfo_snd_cwnd\",\n            \"connection.tcpinfo.send.cwnd\", \"BYTES\",\n            STRING_OR_LONG, FORMAT_NUMBER));\n\n        // $tcpinfo_rcv_space\n        parsers.add(new TokenParser(\"$tcpinfo_rcv_space\",\n            \"connection.tcpinfo.receive.space\", \"BYTES\",\n            STRING_OR_LONG, FORMAT_NUMBER));\n\n        //   // Some explicit type overrides.\n        //   // The '1' at the end indicates this is more important than the default TokenParser (which has an implicit 0).\n        //   parsers.add(new TokenParser(\"%{cookie}i\",\n        //           \"request.cookies\", \"HTTP.COOKIES\",\n        //           STRING_ONLY, FORMAT_STRING, 1));\n        //   parsers.add(new TokenParser(\"%{set-cookie}o\",\n        //           \"response.cookies\", \"HTTP.SETCOOKIES\",\n        //           STRING_ONLY, FORMAT_STRING, 1));\n\n        // -------\n        // Fallback for all unknown variables that might appear\n        parsers.add(new NamedTokenParser(\"\\\\$([a-z0-9\\\\-\\\\_]*)\",\n            \"nginx.unknown.\", \"UNKNOWN_NGINX_VARIABLE\",\n            STRING_ONLY, FORMAT_NO_SPACE_STRING, -10)\n            .setWarningMessageWhenUsed(\"Found unknown variable \\\"${}\\\" that was mapped to \\\"{}\\\". \" +\n                \"It is assumed the values are text that cannot contain a whitespace.\"));\n\n        return parsers;\n    }\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/main/java/nl/basjes/parse/httpdlog/dissectors/nginxmodules/GeoIPModule.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage nl.basjes.parse.httpdlog.dissectors.nginxmodules;\n\nimport nl.basjes.parse.httpdlog.dissectors.tokenformat.TokenParser;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport static nl.basjes.parse.core.Casts.STRING_ONLY;\nimport static nl.basjes.parse.httpdlog.dissectors.tokenformat.TokenParser.FORMAT_NO_SPACE_STRING;\nimport static nl.basjes.parse.httpdlog.dissectors.tokenformat.TokenParser.FORMAT_STRING;\n\n// Implement the variables described here:\n// https://nginx.org/en/docs/http/ngx_http_geoip_module.html\npublic class GeoIPModule implements NginxModule {\n\n    private static final String PREFIX = \"nginxmodule.geoip\";\n\n    @Override\n    public List<TokenParser> getTokenParsers() {\n        List<TokenParser> parsers = new ArrayList<>(60);\n\n        // $geoip_country_code\n        // two-letter country code, for example, “RU”, “US”.\n        parsers.add(new TokenParser(\"$geoip_country_code\", PREFIX + \".country.code\", \"STRING\", STRING_ONLY, FORMAT_NO_SPACE_STRING));\n\n        // $geoip_country_code3\n        // three-letter country code, for example, “RUS”, “USA”.\n        parsers.add(new TokenParser(\"$geoip_country_code3\", PREFIX + \".country.code3\", \"STRING\", STRING_ONLY, FORMAT_NO_SPACE_STRING));\n\n        // $geoip_country_name\n        // country name, for example, “Russian Federation”, “United States”.\n        parsers.add(new TokenParser(\"$geoip_country_name\", PREFIX + \".country.name\", \"STRING\", STRING_ONLY, FORMAT_STRING));\n\n        // $geoip_area_code telephone area code (US only).\n        // This variable may contain outdated information since the corresponding database field is deprecated.\n        parsers.add(new TokenParser(\"$geoip_area_code\", PREFIX + \".area.code\", \"STRING\", STRING_ONLY, FORMAT_NO_SPACE_STRING));\n\n        // $geoip_city_continent_code\n        // two-letter continent code, for example, “EU”, “NA”.\n        parsers.add(new TokenParser(\"$geoip_city_continent_code\", PREFIX + \".continent.code\", \"STRING\", STRING_ONLY, FORMAT_NO_SPACE_STRING));\n\n        // $geoip_city_country_code\n        // two-letter country code, for example, “RU”, “US”.\n        parsers.add(new TokenParser(\"$geoip_city_country_code\", PREFIX + \".country.code\", \"STRING\", STRING_ONLY, FORMAT_NO_SPACE_STRING));\n\n        // $geoip_city_country_code3\n        // three-letter country code, for example, “RUS”, “USA”.\n        parsers.add(new TokenParser(\"$geoip_city_country_code3\", PREFIX + \".country.code3\", \"STRING\", STRING_ONLY, FORMAT_NO_SPACE_STRING));\n\n        // $geoip_city_country_name\n        // country name, for example, “Russian Federation”, “United States”.\n        parsers.add(new TokenParser(\"$geoip_city_country_name\", PREFIX + \".country.name\", \"STRING\", STRING_ONLY, FORMAT_STRING));\n\n        // $geoip_dma_code\n        // DMA region code in US (also known as “metro code”), according to the geotargeting in Google AdWords API.\n        parsers.add(new TokenParser(\"$geoip_dma_code\", PREFIX + \".dma.code\", \"STRING\", STRING_ONLY, FORMAT_STRING));\n\n        // $geoip_latitude\n        // latitude.\n        parsers.add(new TokenParser(\"$geoip_latitude\", PREFIX + \".location.latitude\", \"STRING\", STRING_ONLY, FORMAT_STRING));\n\n        // $geoip_longitude\n        // longitude.\n        parsers.add(new TokenParser(\"$geoip_longitude\", PREFIX + \".location.longitude\", \"STRING\", STRING_ONLY, FORMAT_STRING));\n\n        // $geoip_region\n        // two-symbol country region code (region, territory, state, province, federal land and the like), for example, “48”, “DC”.\n        parsers.add(new TokenParser(\"$geoip_region\", PREFIX + \".region.code\", \"STRING\", STRING_ONLY, FORMAT_NO_SPACE_STRING));\n\n        // $geoip_region_name\n        // country region name (region, territory, state, province, federal land and the like), for example, “Moscow City”, “District of Columbia”.\n        parsers.add(new TokenParser(\"$geoip_region_name\", PREFIX + \".region.name\", \"STRING\", STRING_ONLY, FORMAT_STRING));\n\n        // $geoip_city\n        // city name, for example, “Moscow”, “Washington”.\n        parsers.add(new TokenParser(\"$geoip_city\", PREFIX + \".city\", \"STRING\", STRING_ONLY, FORMAT_STRING));\n\n        // $geoip_postal_code\n        // postal code.\n        parsers.add(new TokenParser(\"$geoip_postal_code\", PREFIX + \".postal.code\", \"STRING\", STRING_ONLY, FORMAT_STRING));\n\n        // $geoip_org\n        // organization name, for example, “The University of Melbourne”.\n        // TODO: Is is unclear is this is the ISP or the ASP organization\n        parsers.add(new TokenParser(\"$geoip_org\", PREFIX + \".organization\", \"STRING\", STRING_ONLY, FORMAT_STRING));\n\n        return parsers;\n    }\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/main/java/nl/basjes/parse/httpdlog/dissectors/nginxmodules/KubernetesIngressModule.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage nl.basjes.parse.httpdlog.dissectors.nginxmodules;\n\nimport nl.basjes.parse.httpdlog.dissectors.tokenformat.TokenParser;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport static nl.basjes.parse.core.Casts.STRING_ONLY;\nimport static nl.basjes.parse.httpdlog.dissectors.tokenformat.TokenParser.FORMAT_STRING;\n\n//\n// https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/log-format/\n//\npublic class KubernetesIngressModule implements NginxModule {\n\n    private static final String PREFIX = \"nginxmodule.kubernetes\";\n\n    @Override\n    public List<TokenParser> getTokenParsers() {\n        List<TokenParser> parsers = new ArrayList<>(60);\n\n        // $the_real_ip\n        // the source IP address of the client\n        parsers.add(new TokenParser(\"$the_real_ip\", PREFIX + \".the_real_ip\", \"IP\", STRING_ONLY, FORMAT_STRING));\n\n        // $proxy_upstream_name\n        // name of the upstream. The format is upstream-<namespace>-<service name>-<service port>\n        parsers.add(new TokenParser(\"$proxy_upstream_name\", PREFIX + \".proxy_upstream_name\", \"STRING\", STRING_ONLY, FORMAT_STRING));\n\n        // $req_id\n        // the randomly generated ID of the request\n        parsers.add(new TokenParser(\"$req_id\", PREFIX + \".req_id\", \"STRING\", STRING_ONLY, FORMAT_STRING));\n\n        // $namespace\n        // namespace of the ingress\n        parsers.add(new TokenParser(\"$namespace\", PREFIX + \".namespace\", \"STRING\", STRING_ONLY, FORMAT_STRING));\n\n        // $ingress_name\n        // name of the ingress\n        parsers.add(new TokenParser(\"$ingress_name\", PREFIX + \".ingress_name\", \"STRING\", STRING_ONLY, FORMAT_STRING));\n\n        // $service_name\n        // name of the service\n        parsers.add(new TokenParser(\"$service_name\", PREFIX + \".service.name\", \"STRING\", STRING_ONLY, FORMAT_STRING));\n\n        // $service_port\n        // port of the service\n        parsers.add(new TokenParser(\"$service_port\", PREFIX + \".service.port\", \"PORT\", STRING_ONLY, FORMAT_STRING));\n\n\n        return parsers;\n    }\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/main/java/nl/basjes/parse/httpdlog/dissectors/nginxmodules/NginxModule.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage nl.basjes.parse.httpdlog.dissectors.nginxmodules;\n\nimport nl.basjes.parse.core.Dissector;\nimport nl.basjes.parse.httpdlog.dissectors.tokenformat.TokenParser;\n\nimport java.util.Collections;\nimport java.util.List;\n\npublic interface NginxModule {\n    List<TokenParser> getTokenParsers();\n\n    default List<Dissector> getDissectors() {\n        return Collections.emptyList(); // By default no extra dissectors\n    }\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/main/java/nl/basjes/parse/httpdlog/dissectors/nginxmodules/SslModule.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage nl.basjes.parse.httpdlog.dissectors.nginxmodules;\n\nimport nl.basjes.parse.httpdlog.dissectors.tokenformat.TokenParser;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport static nl.basjes.parse.core.Casts.STRING_ONLY;\nimport static nl.basjes.parse.httpdlog.dissectors.tokenformat.TokenParser.FORMAT_NO_SPACE_STRING;\nimport static nl.basjes.parse.httpdlog.dissectors.tokenformat.TokenParser.FORMAT_STRING;\n\n// Implement the tokens described here:\n// https://nginx.org/en/docs/http/ngx_http_upstream_module.html#variables\n// https://nginx.org/en/docs/stream/ngx_stream_upstream_module.html#variables\n// https://nginx.org/en/docs/stream/ngx_stream_ssl_preread_module.html#variables\npublic class SslModule implements NginxModule {\n\n    private static final String PREFIX = \"nginxmodule.ssl\";\n\n    @Override\n    public List<TokenParser> getTokenParsers() {\n        List<TokenParser> parsers = new ArrayList<>(60);\n\n        // $ssl_cipher\n        // returns the string of ciphers used for an established SSL connection;\n        parsers.add(new TokenParser(\"$ssl_cipher\",\n                    PREFIX + \".cipher\", \"STRING\",\n                    STRING_ONLY, FORMAT_STRING));\n\n        // $ssl_ciphers\n        // returns the list of ciphers supported by the client (1.11.7).\n        // Known ciphers are listed by names, unknown are shown in hexadecimal,\n        // for example: AES128-SHA:AES256-SHA:0x00ff\n        parsers.add(new TokenParser(\"$ssl_ciphers\",\n                    PREFIX + \".client.ciphers\", \"STRING\",\n                    STRING_ONLY, FORMAT_STRING));\n\n        // $ssl_client_escaped_cert\n        // returns the client certificate in the PEM format (urlencoded) for an established SSL connection (1.13.5);\n        parsers.add(new TokenParser(\"$ssl_client_escaped_cert\",\n                    PREFIX + \".client.cert\", \"PEM_CERT_URLENCODED\",\n                    STRING_ONLY, FORMAT_NO_SPACE_STRING));\n\n        // $ssl_client_cert --> IS DEPRECATED USE $ssl_client_escaped_cert\n        // returns the client certificate in the PEM format for an established SSL connection,\n        // with each line except the first prepended with the tab character;\n        // this is intended for the use in the proxy_set_header directive;\n        // The variable is deprecated, the $ssl_client_escaped_cert variable should be used instead.\n        parsers.add(new TokenParser(\"$ssl_client_cert\",\n                    PREFIX + \".client.cert\", \"PEM_CERT\",\n                    STRING_ONLY, FORMAT_STRING));\n\n        // $ssl_client_raw_cert\n        // returns the client certificate in the PEM format for an established SSL connection;\n        parsers.add(new TokenParser(\"$ssl_client_raw_cert\",\n                    PREFIX + \".client.cert\", \"PEM_CERT_RAW\",\n                    STRING_ONLY, FORMAT_STRING));\n\n        // $ssl_client_fingerprint\n        // returns the SHA1 fingerprint of the client certificate for an established SSL connection (1.7.1);\n        parsers.add(new TokenParser(\"$ssl_client_fingerprint\",\n                    PREFIX + \".client.cert.fingerprint\", \"SHA1\",\n                    STRING_ONLY, FORMAT_NO_SPACE_STRING));\n\n        // $ssl_client_i_dn\n        // returns the “issuer DN” string of the client certificate for an established SSL connection according to RFC 2253 (1.11.6);\n        parsers.add(new TokenParser(\"$ssl_client_i_dn\",\n                    PREFIX + \".client.cert.issuer_dn\", \"STRING\",\n                    STRING_ONLY, FORMAT_STRING));\n\n        // $ssl_client_i_dn_legacy\n        // returns the “issuer DN” string of the client certificate for an established SSL connection;\n        // Prior to version 1.11.6, the variable name was $ssl_client_i_dn.\n        parsers.add(new TokenParser(\"$ssl_client_i_dn_legacy\",\n                    PREFIX + \".client.cert.issuer_dn.legacy\", \"STRING\",\n                    STRING_ONLY, FORMAT_STRING));\n\n        // $ssl_client_s_dn\n        // returns the “subject DN” string of the client certificate for an established SSL connection according to RFC 2253 (1.11.6);\n        parsers.add(new TokenParser(\"$ssl_client_s_dn\",\n                    PREFIX + \".client.cert.subject_dn\", \"STRING\",\n                    STRING_ONLY, FORMAT_STRING));\n\n        // $ssl_client_s_dn_legacy\n        // returns the “subject DN” string of the client certificate for an established SSL connection;\n        // Prior to version 1.11.6, the variable name was $ssl_client_s_dn.\n        parsers.add(new TokenParser(\"$ssl_client_s_dn_legacy\",\n                    PREFIX + \".client.cert.subject_dn.legacy\", \"STRING\",\n                    STRING_ONLY, FORMAT_STRING));\n\n        // $ssl_client_serial\n        // returns the serial number of the client certificate for an established SSL connection;\n        parsers.add(new TokenParser(\"$ssl_client_serial\",\n                    PREFIX + \".client.cert.serial\", \"STRING\",\n                    STRING_ONLY, FORMAT_STRING));\n\n        // $ssl_client_v_end\n        // returns the end date of the client certificate (1.11.7);\n        parsers.add(new TokenParser(\"$ssl_client_v_end\",\n                    PREFIX + \".client.cert.end_date\", \"STRING\", // TODO: What is the format of this date?\n                    STRING_ONLY, FORMAT_STRING));\n\n        // $ssl_client_v_remain\n        // returns the number of days until the client certificate expires (1.11.7);\n        parsers.add(new TokenParser(\"$ssl_client_v_remain\",\n                    PREFIX + \".client.cert.remain_days\", \"STRING\", // TODO: What is the format of this?\n                    STRING_ONLY, FORMAT_STRING));\n\n        // $ssl_client_v_start\n        // returns the start date of the client certificate (1.11.7);\n        parsers.add(new TokenParser(\"$ssl_client_v_start\",\n                    PREFIX + \".client.cert.start_date\", \"STRING\", // TODO: What is the format of this date?\n                    STRING_ONLY, FORMAT_STRING));\n\n        // $ssl_client_verify\n        // returns the result of client certificate verification:\n        //      “SUCCESS”, “FAILED:reason”, and “NONE” if a certificate was not present;\n        // Prior to version 1.11.7, the “FAILED” result did not contain the reason string.\n        parsers.add(new TokenParser(\"$ssl_client_verify\",\n                    PREFIX + \".client.cert.verify\", \"STRING\",\n                    STRING_ONLY, FORMAT_STRING));\n\n        // $ssl_curves\n        // returns the list of curves supported by the client (1.11.7).\n        // Known curves are listed by names, unknown are shown in hexadecimal,\n        // for example: 0x001d:prime256v1:secp521r1:secp384r1\n        // The variable is supported only when using OpenSSL version 1.0.2 or higher.\n        // With older versions, the variable value will be an empty string. The variable is available only for new sessions.\n        parsers.add(new TokenParser(\"$ssl_curves\",\n                    PREFIX + \".client.curves\", \"STRING\",\n                    STRING_ONLY, FORMAT_STRING));\n\n        // $ssl_early_data\n        // returns “1” if TLS 1.3 early data is used and the handshake is not complete, otherwise “” (1.15.3).\n        parsers.add(new TokenParser(\"$ssl_early_data\",\n                    PREFIX + \".early_data\", \"STRING\",\n                    STRING_ONLY, \"1?\"));\n\n        // $ssl_protocol\n        // returns the protocol of an established SSL connection;\n        parsers.add(new TokenParser(\"$ssl_protocol\",\n                    PREFIX + \".protocol\", \"STRING\",\n                    STRING_ONLY, FORMAT_STRING));\n\n        // $ssl_server_name\n        // returns the server name requested through SNI (1.7.0);\n        parsers.add(new TokenParser(\"$ssl_server_name\",\n                    PREFIX + \".server_name\", \"STRING\",\n                    STRING_ONLY, FORMAT_STRING));\n\n        // $ssl_session_id\n        // returns the session identifier of an established SSL connection;\n        parsers.add(new TokenParser(\"$ssl_session_id\",\n                    PREFIX + \".session.id\", \"STRING\",\n                    STRING_ONLY, FORMAT_STRING));\n\n        // $ssl_session_reused\n        // returns “r” if an SSL session was reused, or “.” otherwise (1.5.11).\n        parsers.add(new TokenParser(\"$ssl_session_reused\",\n                    PREFIX + \".session.reused\", \"STRING\",\n                    STRING_ONLY, \"(r|.)\"));\n\n        // $ssl_preread_protocol\n        // the highest SSL protocol version supported by the client (1.15.2)\n        parsers.add(new TokenParser(\"$ssl_preread_protocol\",\n                    PREFIX + \".preread.protocol\", \"STRING\",\n                    STRING_ONLY, FORMAT_STRING));\n\n        // $ssl_preread_server_name\n        // server name requested through SNI\n        parsers.add(new TokenParser(\"$ssl_preread_server_name\",\n                    PREFIX + \".preread.server_name\", \"STRING\",\n                    STRING_ONLY, FORMAT_STRING));\n\n        // $ssl_preread_alpn_protocols\n        // list of protocols advertised by the client through ALPN (1.13.10).\n        // The values are separated by commas.\n        parsers.add(new TokenParser(\"$ssl_preread_alpn_protocols\",\n                    PREFIX + \".preread.alpn_protocols\", \"STRING\",\n                    STRING_ONLY, FORMAT_STRING));\n\n        return parsers;\n    }\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/main/java/nl/basjes/parse/httpdlog/dissectors/nginxmodules/UpstreamListDissector.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage nl.basjes.parse.httpdlog.dissectors.nginxmodules;\n\nimport nl.basjes.parse.core.Casts;\nimport nl.basjes.parse.core.Dissector;\nimport nl.basjes.parse.core.Parsable;\nimport nl.basjes.parse.core.ParsedField;\nimport nl.basjes.parse.core.exceptions.DissectionFailure;\nimport nl.basjes.parse.core.exceptions.InvalidDissectorException;\n\nimport java.util.ArrayList;\nimport java.util.EnumSet;\nimport java.util.List;\n\nimport static nl.basjes.parse.core.Casts.NO_CASTS;\n\n// Implement the tokens described here:\n// https://nginx.org/en/docs/http/ngx_http_upstream_module.html#variables\n\n// $upstream_addr\n// keeps the IP address and port, or the path to the UNIX-domain socket of the upstream server.\n// If several servers were contacted during request processing, their addresses are separated by commas,\n// e.g. “192.168.1.1:80, 192.168.1.2:80, unix:/tmp/sock”.\n//\n// If an internal redirect from one server group to another happens, initiated by “X-Accel-Redirect”\n// or error_page, then the server addresses from different groups are separated by colons,\n// e.g. “192.168.1.1:80, 192.168.1.2:80, unix:/tmp/sock : 192.168.10.1:80, 192.168.10.2:80”.\n//\n// If a server cannot be selected, the variable keeps the name of the server group.\n\n// This dissector tries to pick apart this list format.\n\npublic class UpstreamListDissector extends Dissector {\n\n    private static final String OUTPUT_ORIGINAL_NAME   = \".value\";\n    private static final String OUTPUT_REDIRECTED_NAME = \".redirected\";\n\n    private String          inputType;\n    private String          outputOriginalType;\n    private EnumSet<Casts>  outputOriginalCasts;\n    private String          outputRedirectedType;\n    private EnumSet<Casts>  outputRedirectedCasts;\n\n    public UpstreamListDissector() {\n        inputType                   = null;\n        outputOriginalType          = null;\n        outputOriginalCasts         = null;\n        outputRedirectedType        = null;\n        outputRedirectedCasts       = null;\n    }\n\n    public UpstreamListDissector(String inputType,\n                                 String outputOriginalType, EnumSet<Casts> outputOriginalCasts,\n                                 String outputRedirectedType, EnumSet<Casts> outputRedirectedCasts) {\n        this.inputType = inputType;\n        this.outputOriginalType     = outputOriginalType;\n        this.outputOriginalCasts    = outputOriginalCasts;\n        this.outputRedirectedType   = outputRedirectedType;\n        this.outputRedirectedCasts  = outputRedirectedCasts;\n    }\n\n    @Override\n    public void dissect(Parsable<?> parsable, String inputname) throws DissectionFailure {\n        final ParsedField field = parsable.getParsableField(inputType, inputname);\n\n        String fieldValue = field.getValue().getString();\n\n        if (fieldValue == null || fieldValue.isEmpty()) {\n            return; // Nothing to do.\n        }\n\n        String[] servers = fieldValue.split(\", \");\n        int serverNr = 0;\n        for (String server: servers) {\n            String[] parts = server.split(\": \");\n            if (parts.length == 1) {\n                parsable.addDissection(inputname,\n                                       outputOriginalType,\n                                       serverNr + OUTPUT_ORIGINAL_NAME,\n                                       parts[0].trim());\n                parsable.addDissection(inputname,\n                                       outputRedirectedType,\n                                       serverNr + OUTPUT_REDIRECTED_NAME,\n                                       parts[0].trim());\n            } else {\n                parsable.addDissection(inputname,\n                                       outputOriginalType,\n                                       serverNr + OUTPUT_ORIGINAL_NAME,\n                                       parts[0].trim());\n                parsable.addDissection(inputname,\n                                       outputRedirectedType,\n                                       serverNr + OUTPUT_REDIRECTED_NAME,\n                                       parts[1].trim());\n            }\n            serverNr++;\n        }\n    }\n\n    @Override\n    public String getInputType() {\n        return inputType;\n    }\n\n    @Override\n    public List<String> getPossibleOutput() {\n        List<String> result = new ArrayList<>();\n\n        for (int i = 0; i < 32; i++){\n            result.add(outputOriginalType   + \":\" + i + OUTPUT_ORIGINAL_NAME);\n            result.add(outputRedirectedType + \":\" + i + OUTPUT_REDIRECTED_NAME);\n        }\n        return result;\n    }\n\n    @Override\n    public EnumSet<Casts> prepareForDissect(String inputname, String outputname) {\n        String name = extractFieldName(inputname, outputname);\n\n        if (name.endsWith(OUTPUT_ORIGINAL_NAME)) {\n            return outputOriginalCasts;\n        }\n        if (name.endsWith(OUTPUT_REDIRECTED_NAME)) {\n            return outputRedirectedCasts;\n        }\n        return NO_CASTS;\n    }\n\n    @Override\n    protected void initializeNewInstance(Dissector newInstance) throws InvalidDissectorException {\n        if (!(newInstance instanceof UpstreamListDissector)) {\n            throw new InvalidDissectorException(\n                \"Called UpstreamListDissector::initializeNewInstance with a dissector of class \" + newInstance.getClass().getCanonicalName());\n        }\n        UpstreamListDissector dissector  = (UpstreamListDissector)newInstance;\n        dissector.inputType              = inputType;\n        dissector.outputOriginalType     = outputOriginalType;\n        dissector.outputOriginalCasts    = outputOriginalCasts;\n        dissector.outputRedirectedType   = outputRedirectedType;\n        dissector.outputRedirectedCasts  = outputRedirectedCasts;\n    }\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/main/java/nl/basjes/parse/httpdlog/dissectors/nginxmodules/UpstreamModule.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage nl.basjes.parse.httpdlog.dissectors.nginxmodules;\n\nimport nl.basjes.parse.core.Dissector;\nimport nl.basjes.parse.httpdlog.dissectors.tokenformat.NamedTokenParser;\nimport nl.basjes.parse.httpdlog.dissectors.tokenformat.TokenParser;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport static nl.basjes.parse.core.Casts.STRING_ONLY;\nimport static nl.basjes.parse.core.Casts.STRING_OR_LONG;\nimport static nl.basjes.parse.core.Casts.STRING_OR_LONG_OR_DOUBLE;\nimport static nl.basjes.parse.httpdlog.dissectors.tokenformat.TokenParser.FORMAT_NO_SPACE_STRING;\nimport static nl.basjes.parse.httpdlog.dissectors.tokenformat.TokenParser.FORMAT_NUMBER;\nimport static nl.basjes.parse.httpdlog.dissectors.tokenformat.TokenParser.FORMAT_NUMBER_DECIMAL;\nimport static nl.basjes.parse.httpdlog.dissectors.tokenformat.TokenParser.FORMAT_STRING;\n\n// Implement the tokens described here:\n// https://nginx.org/en/docs/http/ngx_http_upstream_module.html#variables\n// https://nginx.org/en/docs/stream/ngx_stream_upstream_module.html#variables\npublic class UpstreamModule implements NginxModule {\n\n    private static final String PREFIX = \"nginxmodule.upstream\";\n\n    private String upstreamListOf(String regex) {\n        return regex + \"(?: *, *\" + regex + \"(?: *: *\" + regex + \")?)*\";\n    }\n\n    private String optionalUpstreamListOf(String regex) {\n        return \"(?:\" + upstreamListOf(regex) + \"|-)\";\n    }\n\n    @Override\n    public List<TokenParser> getTokenParsers() {\n        List<TokenParser> parsers = new ArrayList<>(60);\n\n        // NOTE: All values are really optional (so they may also be a '-') because the required 'upstream' module\n        //       is not guaranteed to be running.\n\n        // $upstream_addr\n        // keeps the IP address and port, or the path to the UNIX-domain socket of the upstream server.\n        // If several servers were contacted during request processing, their addresses are separated by commas,\n        // e.g. “192.168.1.1:80, 192.168.1.2:80, unix:/tmp/sock”.\n        //\n        // If an internal redirect from one server group to another happens, initiated by “X-Accel-Redirect”\n        // or error_page, then the server addresses from different groups are separated by colons,\n        // e.g. “192.168.1.1:80, 192.168.1.2:80, unix:/tmp/sock : 192.168.10.1:80, 192.168.10.2:80”.\n        //\n        // If a server cannot be selected, the variable keeps the name of the server group.\n        parsers.add(new TokenParser(\"$upstream_addr\",\n            PREFIX + \".addr\", \"UPSTREAM_ADDR_LIST\",\n            STRING_ONLY, optionalUpstreamListOf(FORMAT_NO_SPACE_STRING)));\n\n        // $upstream_bytes_received\n        // number of bytes received from an upstream server.\n        // Values from several connections are separated by commas and colons like addresses\n        // in the $upstream_addr variable.\n        parsers.add(new TokenParser(\"$upstream_bytes_received\",\n            PREFIX + \".bytes.received\", \"UPSTREAM_BYTES_LIST\",\n            STRING_ONLY, optionalUpstreamListOf(FORMAT_NUMBER)));\n\n        // $upstream_bytes_sent\n        // number of bytes sent to an upstream server.\n        // Values from several connections are separated by commas and colons like addresses\n        // in the $upstream_addr variable.\n        parsers.add(new TokenParser(\"$upstream_bytes_sent\",\n            PREFIX + \".bytes.sent\", \"UPSTREAM_BYTES_LIST\",\n            STRING_ONLY, optionalUpstreamListOf(FORMAT_NUMBER)));\n\n        // $upstream_cache_status\n        // keeps the status of accessing a response cache.\n        // The status can be either “MISS”, “BYPASS”, “EXPIRED”, “STALE”, “UPDATING”, “REVALIDATED”, or “HIT”.\n        parsers.add(new TokenParser(\"$upstream_cache_status\",\n            PREFIX + \".cache.status\", \"UPSTREAM_CACHE_STATUS\",\n            STRING_ONLY, \"(?:MISS|BYPASS|EXPIRED|STALE|UPDATING|REVALIDATED|HIT)\"));\n\n        // $upstream_connect_time\n        // keeps time spent on establishing a connection with the upstream server (1.9.1);\n        // the time is kept in seconds with millisecond resolution. In case of SSL, includes time spent on handshake.\n        // Times of several connections are separated by commas and colons like addresses in the $upstream_addr variable.\n        parsers.add(new TokenParser(\"$upstream_connect_time\",\n            PREFIX + \".connect.time\", \"UPSTREAM_SECOND_MILLIS_LIST\",\n            STRING_ONLY, optionalUpstreamListOf(FORMAT_NUMBER_DECIMAL)));\n\n        // $upstream_cookie_name\n        // cookie with the specified name sent by the upstream server in the “Set-Cookie” response header field (1.7.1).\n        // Only the cookies from the response of the last server are saved.\n        parsers.add(new NamedTokenParser(\"\\\\$upstream_cookie_([a-z0-9\\\\-_]*)\",\n            PREFIX + \".response.cookies.\", \"HTTP.COOKIE\",\n            STRING_ONLY, FORMAT_STRING));\n\n        // $upstream_header_time\n        // keeps time spent on receiving the response header from the upstream server (1.7.10);\n        // the time is kept in seconds with millisecond resolution.\n        // Times of several responses are separated by commas and colons like addresses in the $upstream_addr variable.\n        parsers.add(new TokenParser(\"$upstream_header_time\",\n            PREFIX + \".header.time\", \"UPSTREAM_SECOND_MILLIS_LIST\",\n            STRING_ONLY, optionalUpstreamListOf(FORMAT_NUMBER_DECIMAL)));\n\n        // $upstream_http_name\n        // keep server response header fields. For example, the “Server” response header field is available through\n        // the $upstream_http_server variable. The rules of converting header field names to variable names are\n        // the same as for the variables that start with the “$http_” prefix.\n        // Only the header fields from the response of the last server are saved.\n        parsers.add(new NamedTokenParser(\"\\\\$upstream_http_([a-z0-9\\\\-_]*)\",\n            PREFIX + \".header.\", \"HTTP.HEADER\",\n            STRING_ONLY, FORMAT_STRING));\n\n        // $upstream_queue_time\n        // keeps time the request spent in the upstream queue (1.13.9);\n        // the time is kept in seconds with millisecond resolution.\n        // Times of several responses are separated by commas and colons like addresses in the $upstream_addr variable.\n        parsers.add(new TokenParser(\"$upstream_queue_time\",\n            PREFIX + \".queue.time\", \"UPSTREAM_SECOND_MILLIS_LIST\",\n            STRING_ONLY, optionalUpstreamListOf(FORMAT_NUMBER_DECIMAL)));\n\n        // $upstream_response_length\n        // keeps the length of the response obtained from the upstream server (0.7.27);\n        // the length is kept in bytes.\n        // Lengths of several responses are separated by commas and colons like addresses in the $upstream_addr variable.\n        parsers.add(new TokenParser(\"$upstream_response_length\",\n            PREFIX + \".response.length\", \"UPSTREAM_BYTES_LIST\",\n            STRING_ONLY, optionalUpstreamListOf(FORMAT_NUMBER)));\n\n        // $upstream_response_time\n        // keeps time spent on receiving the response from the upstream server;\n        // the time is kept in seconds with millisecond resolution.\n        // Times of several responses are separated by commas and colons like addresses in the $upstream_addr variable.\n        parsers.add(new TokenParser(\"$upstream_response_time\",\n            PREFIX + \".response.time\", \"UPSTREAM_SECOND_MILLIS_LIST\",\n            STRING_ONLY, optionalUpstreamListOf(FORMAT_NUMBER_DECIMAL)));\n\n        // $upstream_status\n        // keeps status code of the response obtained from the upstream server.\n        // Status codes of several responses are separated by commas and colons like addresses in the $upstream_addr\n        // variable. If a server cannot be selected, the variable keeps the 502 (Bad Gateway) status code.\n        parsers.add(new TokenParser(\"$upstream_status\",\n            PREFIX + \".status\", \"UPSTREAM_STATUS_LIST\",\n            STRING_ONLY, optionalUpstreamListOf(FORMAT_NO_SPACE_STRING)));\n\n        // $upstream_trailer_name\n        // keeps fields from the end of the response obtained from the upstream server (1.13.10).\n        parsers.add(new NamedTokenParser(\"\\\\$upstream_trailer_([a-z0-9\\\\-_]*)\",\n            PREFIX + \".trailer.\", \"HTTP.TRAILER\",\n            STRING_ONLY, FORMAT_STRING));\n\n        // $upstream_first_byte_time\n        // time to receive the first byte of data (1.11.4); the time is kept in seconds with millisecond resolution.\n        // Times of several connections are separated by commas like addresses in the $upstream_addr variable.\n        parsers.add(new TokenParser(\"$upstream_first_byte_time\",\n            PREFIX + \".first_byte.time\", \"UPSTREAM_SECOND_MILLIS_LIST\",\n            STRING_ONLY, optionalUpstreamListOf(FORMAT_NUMBER_DECIMAL)));\n\n        // $upstream_session_time\n        // session duration in seconds with millisecond resolution (1.11.4).\n        // Times of several connections are separated by commas like addresses in the $upstream_addr variable.\n        parsers.add(new TokenParser(\"$upstream_session_time\",\n            PREFIX + \".session.time\", \"UPSTREAM_SECOND_MILLIS_LIST\",\n            STRING_ONLY, optionalUpstreamListOf(FORMAT_NUMBER_DECIMAL)));\n\n        return parsers;\n    }\n\n    @Override\n    public List<Dissector> getDissectors() {\n        List<Dissector> dissectors = new ArrayList<>();\n\n        dissectors.add(new UpstreamListDissector(\n            \"UPSTREAM_ADDR_LIST\",\n            \"UPSTREAM_ADDR\",            STRING_ONLY,\n            \"UPSTREAM_ADDR\",            STRING_ONLY));\n\n        dissectors.add(new UpstreamListDissector(\n            \"UPSTREAM_BYTES_LIST\",\n            \"BYTES\",                    STRING_OR_LONG,\n            \"BYTES\",                    STRING_OR_LONG));\n\n        dissectors.add(new UpstreamListDissector(\n            \"UPSTREAM_SECOND_MILLIS_LIST\",\n            \"SECOND_MILLIS\",            STRING_OR_LONG_OR_DOUBLE,\n            \"SECOND_MILLIS\",            STRING_OR_LONG_OR_DOUBLE));\n\n        dissectors.add(new UpstreamListDissector(\n            \"UPSTREAM_STATUS_LIST\",\n            \"UPSTREAM_STATUS\",          STRING_ONLY,\n            \"UPSTREAM_STATUS\",          STRING_ONLY));\n\n        return dissectors;\n    }\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/main/java/nl/basjes/parse/httpdlog/dissectors/nginxmodules/VariousModule.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage nl.basjes.parse.httpdlog.dissectors.nginxmodules;\n\nimport nl.basjes.parse.httpdlog.dissectors.tokenformat.NamedTokenParser;\nimport nl.basjes.parse.httpdlog.dissectors.tokenformat.TokenParser;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport static nl.basjes.parse.core.Casts.STRING_ONLY;\nimport static nl.basjes.parse.core.Casts.STRING_OR_LONG;\nimport static nl.basjes.parse.httpdlog.dissectors.tokenformat.TokenParser.FORMAT_NO_SPACE_STRING;\nimport static nl.basjes.parse.httpdlog.dissectors.tokenformat.TokenParser.FORMAT_NUMBER_OPTIONAL_DECIMAL;\nimport static nl.basjes.parse.httpdlog.dissectors.tokenformat.TokenParser.FORMAT_STRING;\n\n// Many nginx modules only have a small number of variables:\npublic class VariousModule implements NginxModule {\n\n    private static final String PREFIX = \"nginxmodule\";\n\n    @Override\n    public List<TokenParser> getTokenParsers() {\n        List<TokenParser> parsers = new ArrayList<>(60);\n\n        // -----------------------------------------------------------------------------------------------------\n        // https://nginx.org/en/docs/http/ngx_http_secure_link_module.html#var_secure_link\n\n        // $secure_link\n        // The status of a link check. The specific value depends on the selected operation mode.\n        parsers.add(new TokenParser(\"$secure_link\", PREFIX + \".secure_link.status\", \"STRING\", STRING_ONLY, FORMAT_STRING));\n        // $secure_link_expires\n        // The lifetime of a link passed in a request; intended to be used only in the secure_link_md5 directive.\n        // --- NOT FOR LOGGING ---\n\n        // -----------------------------------------------------------------------------------------------------\n        // https://nginx.org/en/docs/http/ngx_http_session_log_module.html#var_session_log_id\n\n        // $session_log_id\n        // current session ID;\n        parsers.add(new TokenParser(\"$session_log_id\", PREFIX + \".session_log.id\", \"STRING\", STRING_ONLY, FORMAT_STRING));\n        // NOT FOR LOGGING      $ session_log_binary_id         current session ID in binary form (16 bytes).\n\n        // -----------------------------------------------------------------------------------------------------\n        // https://nginx.org/en/docs/http/ngx_http_slice_module.html#var_slice_range\n        // $slice_range\n        // the current slice range in HTTP byte range format, for example, bytes=0-1048575.\n        parsers.add(new TokenParser(\"$slice_range\", PREFIX + \".slice_range\", \"STRING\", STRING_ONLY, FORMAT_STRING));\n\n        // -----------------------------------------------------------------------------------------------------\n        // https://nginx.org/en/docs/http/ngx_http_proxy_module.html#var_proxy_add_x_forwarded_for\n\n        // $proxy_host\n        // name and port of a proxied server as specified in the proxy_pass directive;\n        parsers.add(new TokenParser(\"$proxy_host\", PREFIX + \".proxy.host\", \"STRING\", STRING_ONLY, FORMAT_NO_SPACE_STRING));\n        // $proxy_port\n        // port of a proxied server as specified in the proxy_pass directive, or the protocol’s default port;\n        parsers.add(new TokenParser(\"$proxy_port\", PREFIX + \".proxy.port\", \"STRING\", STRING_ONLY, FORMAT_NO_SPACE_STRING));\n        // $proxy_add_x_forwarded_for\n        // the “X-Forwarded-For” client request header field with the $remote_addr variable appended to it, separated by a comma.\n        // If the “X-Forwarded-For” field is not present in the client request header,\n        // the $proxy_add_x_forwarded_for variable is equal to the $remote_addr variable.\n        parsers.add(new TokenParser(\"$proxy_add_x_forwarded_for\",\n            PREFIX + \".proxy.add_x_forwarded_for\", \"STRING\", STRING_ONLY, FORMAT_NO_SPACE_STRING));\n\n        // -----------------------------------------------------------------------------------------------------\n        // https://nginx.org/en/docs/http/ngx_http_userid_module.html#var_uid_got\n\n        // $uid_got\n        // The cookie name and received client identifier.\n        parsers.add(new TokenParser(\"$uid_got\", PREFIX + \".userid.uid_got\", \"STRING\", STRING_ONLY, FORMAT_STRING));\n        // $uid_reset\n        // If the variable is set to a non-empty string that is not “0”, the client identifiers are reset.\n        // The special value “log” additionally leads to the output of messages about the reset identifiers to the error_log.\n        parsers.add(new TokenParser(\"$uid_reset\", PREFIX + \".userid.uid_reset\", \"STRING\", STRING_ONLY, FORMAT_STRING));\n        // $uid_set\n        // The cookie name and sent client identifier.\n        parsers.add(new TokenParser(\"$uid_set\", PREFIX + \".userid.uid_set\", \"STRING\", STRING_ONLY, FORMAT_STRING));\n\n        // -----------------------------------------------------------------------------------------------------\n        // https://nginx.org/en/docs/http/ngx_http_browser_module.html#var_msie\n\n        // $modern_browser\n        // equals the value set by the modern_browser_value directive, if a browser was identified as modern;\n        parsers.add(new TokenParser(\"$modern_browser\", PREFIX + \".browser.modern\", \"STRING\", STRING_ONLY, FORMAT_STRING));\n        // $ancient_browser\n        // equals the value set by the ancient_browser_value directive, if a browser was identified as ancient;\n        parsers.add(new TokenParser(\"$ancient_browser\", PREFIX + \".browser.ancient\", \"STRING\", STRING_ONLY, FORMAT_STRING));\n        // $msie\n        // equals “1” if a browser was identified as MSIE of any version.\n        parsers.add(new TokenParser(\"$msie\", PREFIX + \".browser.msie\", \"STRING\", STRING_ONLY, FORMAT_NO_SPACE_STRING));\n\n        // -----------------------------------------------------------------------------------------------------\n        // https://nginx.org/en/docs/http/ngx_http_stub_status_module.html#var_connections_active\n\n        // $connections_active\n        // The current number of active client connections including Waiting connections.\n        parsers.add(new TokenParser(\"$connections_active\", PREFIX + \".stub_status.connections.active\", \"STRING\", STRING_ONLY, FORMAT_STRING));\n        // $connections_reading\n        // The current number of connections where nginx is reading the request header.\n        parsers.add(new TokenParser(\"$connections_reading\", PREFIX + \".stub_status.connections.reading\", \"STRING\", STRING_ONLY, FORMAT_STRING));\n        // $connections_writing\n        // The current number of connections where nginx is writing the response back to the client.\n        parsers.add(new TokenParser(\"$connections_writing\", PREFIX + \".stub_status.connections.writing\", \"STRING\", STRING_ONLY, FORMAT_STRING));\n        // $connections_waiting\n        // The current number of idle client connections waiting for a request.\n        parsers.add(new TokenParser(\"$connections_waiting\", PREFIX + \".stub_status.connections.waiting\", \"STRING\", STRING_ONLY, FORMAT_STRING));\n\n        // -----------------------------------------------------------------------------------------------------\n        // https://nginx.org/en/docs/http/ngx_http_ssi_module.html#var_date_gmt\n        // NOTE: These two are STRING because the actual format is unknown (it is configurable)\n\n        // $date_local\n        // current time in the local time zone. The format is set by the config command with the timefmt parameter.\n        parsers.add(new TokenParser(\"$date_local\", PREFIX + \".date.local\", \"STRING\", STRING_ONLY, FORMAT_STRING));\n\n        // $date_gmt\n        // current time in GMT. The format is set by the config command with the timefmt parameter.\n        parsers.add(new TokenParser(\"$date_gmt\", PREFIX + \".date.gmt\", \"STRING\", STRING_ONLY, FORMAT_STRING));\n\n        // -----------------------------------------------------------------------------------------------------\n        // $fastcgi_script_name\n\n        // request URI or, if a URI ends with a slash, request URI with an index file name configured by the fastcgi_index\n        // directive appended to it.\n        parsers.add(new TokenParser(\"$fastcgi_script_name\", PREFIX + \".fastcgi.script_name\", \"STRING\", STRING_ONLY, FORMAT_STRING));\n\n        // $fastcgi_path_info\n        // the value of the second capture set by the fastcgi_split_path_info directive. This variable can be used to set the PATH_INFO parameter.\n        parsers.add(new TokenParser(\"$fastcgi_path_info\", PREFIX + \".fastcgi.path_info\", \"STRING\", STRING_ONLY, FORMAT_STRING));\n\n        // -----------------------------------------------------------------------------------------------------\n        // https://nginx.org/en/docs/http/ngx_http_gzip_module.html#var_gzip_ratio\n\n        // $gzip_ratio\n        // achieved compression ratio, computed as the ratio between the original and compressed response sizes.\n        parsers.add(new TokenParser(\"$gzip_ratio\", PREFIX + \".gzip.ratio\", \"STRING\", STRING_ONLY, FORMAT_NUMBER_OPTIONAL_DECIMAL));\n\n        // -----------------------------------------------------------------------------------------------------\n        // https://nginx.org/en/docs/http/ngx_http_spdy_module.html#var_spdy\n\n        // $spdy\n        // SPDY protocol version for SPDY connections, or an empty string otherwise;\n        parsers.add(new TokenParser(\"$spdy\", PREFIX + \".spdy.version\", \"STRING\", STRING_ONLY, FORMAT_STRING));\n        // $spdy_request_priority\n        // request priority for SPDY connections, or an empty string otherwise.\n        parsers.add(new TokenParser(\"$spdy_request_priority\", PREFIX + \".spdy.request_priority\", \"STRING\", STRING_ONLY, FORMAT_STRING));\n\n        // -----------------------------------------------------------------------------------------------------\n        // https://nginx.org/en/docs/http/ngx_http_v2_module.html#var_http2\n        // $http2\n        // negotiated protocol identifier: “h2” for HTTP/2 over TLS, “h2c” for HTTP/2 over cleartext TCP, or an empty string otherwise.\n        parsers.add(new TokenParser(\"$http2\", PREFIX + \".http2.negotiated_protocol\", \"STRING\", STRING_ONLY, FORMAT_STRING));\n\n        // -----------------------------------------------------------------------------------------------------\n        // https://nginx.org/en/docs/http/ngx_http_referer_module.html#var_invalid_referer\n        // $invalid_referer\n        // Empty string, if the “Referer” request header field value is considered valid, otherwise “1”.\n        parsers.add(new TokenParser(\"$invalid_referer\", PREFIX + \".referer.invalid\", \"STRING\", STRING_ONLY, \"1?\"));\n\n        // -----------------------------------------------------------------------------------------------------\n\n        // https://nginx.org/en/docs/http/ngx_http_auth_jwt_module.html#var_jwt_claim_\n        // $jwt_header_name\n        // returns the value of a specified JOSE header\n        parsers.add(new NamedTokenParser(\"\\\\$jwt_header_([a-z0-9\\\\-_]*)\",\n            PREFIX + \".jwt.header.\", \"STRING\",\n            STRING_ONLY, FORMAT_STRING));\n\n        // $jwt_claim_name\n        // returns the value of a specified JWT claim\n        parsers.add(new NamedTokenParser(\"\\\\$jwt_claim_([a-z0-9\\\\-_]*)\",\n            PREFIX + \".jwt.claim.\", \"STRING\",\n            STRING_ONLY, FORMAT_STRING));\n\n        // -----------------------------------------------------------------------------------------------------\n        // https://nginx.org/en/docs/http/ngx_http_memcached_module.html#var_memcached_key\n\n        // $memcached_key\n        // Defines a key for obtaining response from a memcached server.\n        parsers.add(new TokenParser(\"$memcached_key\", PREFIX + \".memcached.key\", \"STRING\", STRING_ONLY, FORMAT_STRING));\n\n        // -----------------------------------------------------------------------------------------------------\n        // https://nginx.org/en/docs/http/ngx_http_realip_module.html#var_realip_remote_addr\n        // https://nginx.org/en/docs/stream/ngx_stream_realip_module.html#var_realip_remote_addr\n\n        // $realip_remote_addr\n        // keeps the original client address\n        parsers.add(new TokenParser(\"$realip_remote_addr\", PREFIX + \".realip.remote_addr\", \"IP\", STRING_ONLY, FORMAT_STRING));\n\n        // $realip_remote_port\n        // keeps the original client port\n        parsers.add(new TokenParser(\"$realip_remote_port\", PREFIX + \".realip.remote_port\", \"PORT\", STRING_OR_LONG, FORMAT_STRING));\n\n        // -----------------------------------------------------------------------------------------------------\n\n        return parsers;\n    }\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/main/java/nl/basjes/parse/httpdlog/dissectors/tokenformat/NamedTokenParser.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.httpdlog.dissectors.tokenformat;\n\nimport nl.basjes.parse.core.Casts;\n\nimport java.util.EnumSet;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\n/**\n * This is a TokenParser where we expect to get the NAME of the field as a group in the regular expression.\n */\npublic class NamedTokenParser extends TokenParser {\n\n    private final Pattern pattern;\n\n    // --------------------------------------------\n\n    public NamedTokenParser(\n            final String nLogFormatToken,\n            final String nValueName,\n            final String nValueType,\n            final EnumSet<Casts> nCasts,\n            final String nRegex) {\n        this(nLogFormatToken, nValueName, nValueType, nCasts, nRegex, 0);\n    }\n\n    public NamedTokenParser(\n            final String nLogFormatToken,\n            final String nValueName,\n            final String nValueType,\n            final EnumSet<Casts> nCasts,\n            final String nRegex,\n            final int prio) {\n        super(nLogFormatToken, nValueName, nValueType, nCasts, nRegex, prio);\n\n        // Compile the regular expression\n        pattern = Pattern.compile(getLogFormatToken());\n    }\n\n    // --------------------------------------------\n\n    @Override\n    public Token getNextToken(final String logFormat, final int startOffset) {\n        final Matcher matcher = pattern.matcher(logFormat.substring(startOffset));\n        if (!matcher.find()) {\n            return null;\n        }\n\n        String fieldName = \"\";\n        if (matcher.groupCount() > 0) {\n            // Retrieve the name\n            fieldName = matcher.group(1);\n        }\n\n        // Retrieve indices of matching string\n        final int start = matcher.start();\n        final int end = matcher.end();\n        // the end is index of the last matching character + 1\n\n        Token token = new Token(\n            getRegex(),\n            startOffset + start, end - start,\n            getPrio());\n\n        for (TokenOutputField tokenOutputField: getOutputFields()) {\n            token.addOutputField(\n                tokenOutputField.getType(),\n                tokenOutputField.getName() + fieldName,\n                tokenOutputField.getCasts());\n        }\n\n        if (warningMessageWhenUsed != null) {\n            token.setWarningMessageWhenUsed(warningMessageWhenUsed.replaceFirst(\"\\\\{}\", fieldName));\n        }\n\n        return token;\n    }\n\n    // --------------------------------------------\n\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/main/java/nl/basjes/parse/httpdlog/dissectors/tokenformat/ParameterizedTokenParser.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.httpdlog.dissectors.tokenformat;\n\nimport nl.basjes.parse.core.Casts;\nimport nl.basjes.parse.core.Dissector;\nimport org.apache.commons.codec.binary.Hex;\n\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\nimport java.util.EnumSet;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\n/**\n * This is a TokenParser where we expect to get the constructor parameter for the provided dissector as a group in\n * the regular expression. As a consequence the type is a generated string to match THIS dissector instance.\n */\npublic class ParameterizedTokenParser extends TokenParser {\n\n    private final Pattern pattern;\n\n    // --------------------------------------------\n\n    public ParameterizedTokenParser(\n            final String nLogFormatToken,\n            final String nValueName,\n            final String nValueType,\n            final EnumSet<Casts> nCasts,\n            final String nRegex,\n            final int prio,\n            final Dissector customDissector) {\n        super(nLogFormatToken, nValueName, nValueType, nCasts, nRegex, prio, customDissector);\n\n        // Compile the regular expression\n        pattern = Pattern.compile(getLogFormatToken());\n    }\n\n    @Override\n    public TokenParser addOutputField(String type, String name, EnumSet<Casts> casts) {\n        if (getOutputFields().isEmpty()) {\n            return super.addOutputField(type, name, casts);\n        }\n        throw new UnsupportedOperationException(\"A ParameterizedTokenParser only supports ONE outputfield.\");\n    }\n\n    @Override\n    public TokenParser addOutputFields(List<TokenOutputField> outputFields) {\n        if (getOutputFields().isEmpty()) {\n            return super.addOutputFields(outputFields);\n        }\n        throw new UnsupportedOperationException(\"A ParameterizedTokenParser only supports ONE outputfield.\");\n    }\n\n    // --------------------------------------------\n\n    @Override\n    public Token getNextToken(final String logFormat, final int startOffset) {\n        final Matcher matcher = pattern.matcher(logFormat.substring(startOffset));\n        if (!matcher.find()) {\n            return null;\n        }\n\n        String fieldName = \"\";\n        if (matcher.groupCount() > 0) {\n            // Retrieve the name\n            fieldName = matcher.group(1);\n        }\n\n        // Retrieve indices of matching string\n        final int start = matcher.start();\n        final int end = matcher.end();\n        // the end is index of the last matching character + 1\n\n        Token token = new Token(\n            getRegex(),\n            startOffset + start, end - start,\n            getPrio());\n\n        for (TokenOutputField tokenOutputField: getOutputFields()) {\n            String fieldType = tokenParameterToTypeName(fieldName);\n            token.addOutputField(\n                tokenParameterToTypeName(fieldName),\n                tokenOutputField.getName(),\n                tokenOutputField.getCasts());\n            addCustomDissector(token, fieldType, fieldName);\n        }\n\n        if (warningMessageWhenUsed != null) {\n            token.setWarningMessageWhenUsed(warningMessageWhenUsed.replaceFirst(\"\\\\{}\", fieldName));\n        }\n\n        return token;\n    }\n\n    // --------------------------------------------\n\n    String tokenParameterToTypeName(String parameter) {\n        return (\n                getOutputFields().get(0).getType() +\n                parameter.replaceAll(\"[^A-Za-z0-9]\", \"\") +\n                \"_\" + stringHashAsHexString(parameter)\n            ).toUpperCase(Locale.ENGLISH);\n    }\n\n    private String stringHashAsHexString(String input) {\n        String result = \"\";\n        try {\n            MessageDigest md = MessageDigest.getInstance(\"MD5\");\n            result = Hex.encodeHexString(md.digest(input.getBytes()));\n        } catch (NoSuchAlgorithmException e) {\n            // Shouldn't happen\n        }\n        return result;\n    }\n\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/main/java/nl/basjes/parse/httpdlog/dissectors/tokenformat/Token.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.httpdlog.dissectors.tokenformat;\n\nimport nl.basjes.parse.core.Casts;\nimport nl.basjes.parse.core.Dissector;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.EnumSet;\nimport java.util.List;\nimport java.util.Set;\n\npublic class Token implements Serializable {\n    private static final Logger LOG = LoggerFactory.getLogger(Token.class);\n\n    private final List<TokenOutputField> outputFields = new ArrayList<>();\n    private final String regex;\n    private final int startPos;\n    private final int length;\n    private final int prio;\n    protected String warningMessageWhenUsed = null;\n\n    // In some cases a token needs a custom dissector.\n    private Dissector customDissector = null;\n\n    public Token(\n        final String nRegex,\n        final int nStartPos,\n        final int nLength,\n        final int nPrio) {\n        regex = nRegex;\n        startPos = nStartPos;\n        length = nLength;\n        prio = nPrio;\n    }\n\n    public Token addOutputField(String type, String name, EnumSet<Casts> casts) {\n        outputFields.add(new TokenOutputField(type, name, casts));\n        return this;\n    }\n\n    public Token addOutputFields(List<TokenOutputField> nOutputFields) {\n        this.outputFields.addAll(nOutputFields);\n        return this;\n    }\n\n    public List<TokenOutputField> getOutputFields() {\n        return outputFields;\n    }\n\n    public boolean canProduceADesiredFieldName(Set<String> desiredNames) {\n        for (TokenOutputField tokenOutputField: outputFields) {\n            if (desiredNames.contains(tokenOutputField.getName())) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    public void setCustomDissector(Dissector dissector) {\n        customDissector = dissector;\n    }\n\n    public Dissector getCustomDissector() {\n        return customDissector;\n    }\n\n    public String getRegex() {\n        return regex;\n    }\n\n    public int getStartPos() {\n        return startPos;\n    }\n\n    public int getLength() {\n        return length;\n    }\n\n\n    public int getPrio() {\n        return prio;\n    }\n\n    public void setWarningMessageWhenUsed(String message) {\n        warningMessageWhenUsed = message;\n    }\n\n    public void tokenWasUsed() {\n        if (warningMessageWhenUsed != null) {\n            LOG.warn(\"------------------------------------------------------------------------\");\n            LOG.warn(warningMessageWhenUsed, outputFields);\n            LOG.warn(\"------------------------------------------------------------------------\");\n        }\n    }\n\n    // This is used by your favorite debugger.\n    @Override\n    public String toString() {\n        return \"{\" + outputFields + \" (\" + startPos + \"+\" + length + \");Prio=\" + prio + \"}\";\n    }\n\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/main/java/nl/basjes/parse/httpdlog/dissectors/tokenformat/TokenFormatDissector.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.httpdlog.dissectors.tokenformat;\n\nimport nl.basjes.parse.core.Casts;\nimport nl.basjes.parse.core.Dissector;\nimport nl.basjes.parse.core.Parsable;\nimport nl.basjes.parse.core.ParsedField;\nimport nl.basjes.parse.core.Parser;\nimport nl.basjes.parse.core.exceptions.DissectionFailure;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.util.ArrayList;\nimport java.util.EnumSet;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Set;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport static nl.basjes.parse.core.Casts.STRING_ONLY;\n\n@SuppressWarnings({\n    \"PMD.LongVariable\", // I like my variable names this way\n    \"PMD.CyclomaticComplexity\", \"PMD.OnlyOneReturn\",\n    \"PMD.BeanMembersShouldSerialize\", // No beans here\n    \"PMD.DataflowAnomalyAnalysis\" // Results in a lot of mostly useless messages.\n})\npublic abstract class TokenFormatDissector extends Dissector {\n\n    private static final Logger LOG = LoggerFactory.getLogger(TokenFormatDissector.class);\n\n    private String       logFormat           = null;\n    private ArrayList<Token>  logFormatUsedTokens = null; // Using ArrayList because it is Serializable\n    private String       logFormatRegEx      = null;\n    private Pattern      logFormatPattern    = null;\n    private boolean      isUsable            = false;\n\n    private List<Token>  logFormatTokens;\n\n    private List<String> outputTypes;\n\n    // --------------------------------------------\n    public static class FixedStringTokenParser extends TokenParser {\n        public FixedStringTokenParser(final String nLogFormatToken, final String nRegEx) {\n            super(nLogFormatToken, nRegEx, 0);\n        }\n\n        @Override\n        public Token getNextToken(String logFormat, int startOffset) {\n            final int pos = logFormat.indexOf(getLogFormatToken(), startOffset);\n            if (pos == -1) {\n                return null;\n            }\n\n            return new FixedStringToken(\n                getRegex(),\n                pos,\n                getLogFormatToken().length(),\n                0)\n                .addOutputFields(getOutputFields());\n        }\n    }\n\n    public static class FixedStringToken extends Token {\n        public FixedStringToken(String nRegex, int nStartPos, int nLength, int nPrio) {\n            super(nRegex, nStartPos, nLength, nPrio);\n        }\n    }\n\n    // --------------------------------------------\n\n    public static class NotImplementedTokenParser extends TokenParser {\n\n        public NotImplementedTokenParser(final String nLogFormatToken, final String fieldPrefix, int nPrio) {\n            this(nLogFormatToken, fieldPrefix, \".*\", nPrio);\n        }\n\n        public NotImplementedTokenParser(final String nLogFormatToken, final String fieldPrefix, final String regEx, int nPrio) {\n            super(nLogFormatToken,\n                fieldPrefix + \"_\" + nLogFormatToken.toLowerCase(Locale.ENGLISH).replaceAll(\"[^a-z0-9_]\", \"_\"),\n                \"NOT_IMPLEMENTED\",\n                STRING_ONLY,\n                regEx,\n                nPrio);\n        }\n    }\n\n    public TokenFormatDissector(final String logFormat) {\n        setLogFormat(logFormat);\n    }\n\n    public TokenFormatDissector() {\n    }\n\n    @Override\n    public boolean initializeFromSettingsParameter(String settings) {\n        setLogFormat(logFormat);\n        return true; // Everything went right\n    }\n\n    @Override\n    protected void initializeNewInstance(Dissector newInstance) {\n        if (newInstance instanceof TokenFormatDissector) {\n            ((TokenFormatDissector)newInstance).setLogFormat(logFormat);\n        } else {\n            LOG.error(\"============================== WTF == {}\", newInstance.getClass().getCanonicalName());\n        }\n    }\n\n    public void setLogFormat(final String logformat) {\n        this.logFormat = logformat;\n\n        // Now we disassemble the format into parts\n        logFormatTokens = parseTokenLogFileDefinition(this.logFormat);\n\n        outputTypes = new ArrayList<>();\n\n        for (final Token token : logFormatTokens) {\n            if (token instanceof FixedStringToken) {\n                continue;\n            }\n\n            List<TokenOutputField> outputFields = token.getOutputFields();\n            if (!outputFields.isEmpty()) {\n                for (TokenOutputField tokenOutputField: outputFields) {\n                    outputTypes.add(tokenOutputField.getType() + ':' + tokenOutputField.getName());\n                }\n            }\n        }\n    }\n\n    public String getLogFormat() {\n        return logFormat;\n    }\n\n    @SuppressWarnings(\"unused\") // Useful for debugging purposes\n    public String getLogFormatRegEx() {\n        return logFormatRegEx;\n    }\n\n    // --------------------------------------------\n\n    private final Set<String> requestedFields = new HashSet<>(16);\n\n    @Override\n    public EnumSet<Casts> prepareForDissect(final String inputName, final String outputName) {\n        requestedFields.add(outputName);\n        for (Token token: logFormatTokens) {\n            for (TokenOutputField tokenOutputField: token.getOutputFields()) {\n                if (outputName.equals(tokenOutputField.getName())) {\n                    tokenOutputField.wasUsed();\n                    return tokenOutputField.getCasts();\n                }\n            }\n        }\n        return STRING_ONLY;\n    }\n\n    // --------------------------------------------\n\n    @Override\n    public void prepareForRun() {\n        // At this point we have all the tokens and now we construct the\n        // complete regex and the list to use when extracting\n        // We build the regexp so that it only extracts the needed parts.\n\n        // Allocated buffer is a bit bigger than needed\n        final StringBuilder regex = new StringBuilder(logFormatTokens.size() * 16);\n\n        logFormatUsedTokens = new ArrayList<>();\n\n        regex.append('^'); // Link to start of the line\n        for (final Token token : logFormatTokens) {\n            token.tokenWasUsed();\n            if (token instanceof FixedStringToken) {\n                // Only insert the fixed part\n                regex.append(Pattern.quote(token.getRegex()));\n            } else if (token.canProduceADesiredFieldName(requestedFields)) {\n                logFormatUsedTokens.add(token);\n                regex.append(\"(\").append(token.getRegex()).append(\")\");\n            } else {\n                regex.append(\"(?:\").append(token.getRegex()).append(\")\");\n            }\n\n        }\n        regex.append('$'); // Link to end of the line\n\n        logFormatRegEx = regex.toString();\n        LOG.debug(\"Source logformat : {}\", logFormat);\n        LOG.debug(\"Used regex       : {}\", logFormatRegEx);\n\n        // Now we compile this expression ONLY ONCE!\n        logFormatPattern = Pattern.compile(logFormatRegEx);\n\n        isUsable = true; // Ready!\n    }\n\n    // --------------------------------------------\n\n    @Override\n    public void setInputType(String newInputType) {\n        this.inputType = newInputType;\n    }\n\n    private String inputType = null;\n\n    @Override\n    public String getInputType() {\n        return inputType;\n    }\n\n    @Override\n    public List<String> getPossibleOutput() {\n        return outputTypes;\n    }\n\n    /**\n     *\n     * @param tokenName Name of the token that was found\n     * @param value The actual value as it is present in the logline\n     * @return The cleaned/decoded/interpreted version of the value.\n     */\n    public abstract String decodeExtractedValue(String tokenName, String value);\n\n    @Override\n    public void dissect(final Parsable<?> parsable, final String inputname) throws DissectionFailure {\n        if (!isUsable) {\n            throw new DissectionFailure(\"Dissector in unusable state\");\n        }\n\n        final ParsedField line = parsable.getParsableField(inputType, inputname);\n\n        // Now we create a matcher for this line\n        final Matcher matcher = logFormatPattern.matcher(line.getValue().getString());\n\n        // Is it all as expected?\n        final boolean matches = matcher.find();\n\n        if (matches) {\n            for (int i = 1; i <= matcher.groupCount(); i++) {\n                String matchedStr = matcher.group(i);\n                Token token = logFormatUsedTokens.get(i-1);\n                for (TokenOutputField tokenOutputField: token.getOutputFields()) {\n                    final String matchedName = tokenOutputField.getName();\n                    final String matchedType = tokenOutputField.getType();\n\n                    parsable.addDissection(inputname, matchedType, matchedName,\n                        decodeExtractedValue(matchedName, matchedStr));\n                }\n            }\n        } else {\n            throw new DissectionFailure(\"The input line does not match the specified log format.\" +\n                    \"Line     : \" + line.getValue() + \"\\n\" +\n                    \"LogFormat: \" + logFormat       + \"\\n\" +\n                    \"RegEx    : \" + logFormatRegEx);\n        }\n\n    }\n\n    // --------------------------------------------\n\n    /**\n     * This should be overridden if there is a need to cleanup the\n     * actual logformat before parsing.\n     * @param tokenLogFormat the 'dirty' logformat\n     * @return the cleaned version of the tokenLogFormat.\n     */\n    protected String cleanupLogFormat(String tokenLogFormat){\n        return tokenLogFormat;\n    }\n\n    // --------------------------------------------\n    @SuppressWarnings({ \"PMD.AvoidInstantiatingObjectsInLoops\",\n        \"PMD.LongVariable\", \"PMD.ExcessiveMethodLength\",\n        \"PMD.DataflowAnomalyAnalysis\", \"PMD.NcssMethodCount\",\n        \"PMD.NPathComplexity\" })\n    private List<Token> parseTokenLogFileDefinition(final String tokenLogFormat) {\n\n        // Add all available parsers\n        final List<TokenParser> tokenParsers = createAllTokenParsers();\n        final List<Token> tokens = new ArrayList<>(50);\n\n        // We first change all the references to headers to lowercase\n        // because we must handle these as \"case insensitive\"\n        String cleanedTokenLogFormat = cleanupLogFormat(tokenLogFormat);\n\n        // Now we let all tokens figure out if they are present in here\n        for (TokenParser tokenParser : tokenParsers) {\n            List<Token> newTokens = tokenParser.getTokens(cleanedTokenLogFormat);\n            if (newTokens != null) {\n                tokens.addAll(newTokens);\n            }\n        }\n\n        // We now have a full list of all matched tokens\n        // ---------------------------------------\n        // We sort them by position of the token in the format specifier\n        tokens.sort(new TokenSorterByStartPos());\n\n        // First we take out the duplicates with a lower prio(=relevance score)\n        final List<Token> kickTokens = new ArrayList<>(50);\n        Token prevToken = null;\n        for (Token token : tokens) {\n            if (prevToken==null){\n                prevToken=token;\n                continue;\n            }\n\n            if (prevToken.getStartPos() == token.getStartPos()) {\n                if (prevToken.getLength() == token.getLength()) {\n                    if (prevToken.getPrio() < token.getPrio()) {\n                        kickTokens.add(prevToken);\n                    } else {\n                        kickTokens.add(token);\n                    }\n                } else {\n                    if (prevToken.getLength() < token.getLength()) {\n                        kickTokens.add(prevToken);\n                    } else {\n                        kickTokens.add(token);\n                    }\n                }\n            } else {\n                // Sometimes we find that a part of a token matches another token aswell.\n                // Example: %{%H}t    Custom Timeformat (only the hour) also matches the protocol token.\n                // So we kick them of they overlap\n                if (prevToken.getStartPos() + prevToken.getLength() > token.getStartPos()) {\n                    kickTokens.add(token);\n                    continue;\n                }\n            }\n            prevToken=token;\n\n        }\n\n        tokens.removeAll(kickTokens);\n\n        final List<Token> allTokens = new ArrayList<>(50);\n        // We now look for the holes and add \"FIXED STRING\" tokens\n        int tokenBegin;\n        int tokenEnd = 0;\n        for (Token token : tokens) {\n            tokenBegin = token.getStartPos();\n            // Space between the begin of the next token and the end of the previous token?\n            if (tokenBegin - tokenEnd > 0) {\n                String separator = cleanedTokenLogFormat.substring(tokenEnd, tokenBegin);\n                Token fixedStringToken = new FixedStringToken(separator, tokenBegin, tokenBegin - tokenEnd, 0);\n                allTokens.add(fixedStringToken);\n            }\n            allTokens.add(token);\n            tokenEnd = tokenBegin + token.getLength();\n        }\n\n        int logFormatLength = cleanedTokenLogFormat.length();\n        if (tokenEnd < logFormatLength) {\n            String separator = cleanedTokenLogFormat.substring(tokenEnd);\n            Token fixedStringToken = new FixedStringToken(separator, tokenEnd, cleanedTokenLogFormat.length() - tokenEnd, 0);\n            allTokens.add(fixedStringToken);\n        }\n\n        return allTokens;\n    }\n\n\n    @Override\n    public <RECORD> void createAdditionalDissectors(Parser<RECORD> parser) {\n        for (Token token: logFormatTokens) {\n            parser.addDissector(token.getCustomDissector());\n        }\n    }\n\n    // --------------------------------------------\n    protected abstract List<TokenParser> createAllTokenParsers();\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/main/java/nl/basjes/parse/httpdlog/dissectors/tokenformat/TokenOutputField.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.httpdlog.dissectors.tokenformat;\n\nimport nl.basjes.parse.core.Casts;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.io.Serializable;\nimport java.util.EnumSet;\n\npublic class TokenOutputField implements Serializable {\n\n    private static final Logger LOG = LoggerFactory.getLogger(TokenOutputField.class);\n\n    private final String type;\n    private final String name;\n    private final EnumSet<Casts> casts;\n\n    /**\n     * If this is not null the string is the name of the replacement field\n     */\n    private String deprecated = null;\n\n    public TokenOutputField(String type, String name, EnumSet<Casts> casts) {\n        // RFC 2616 Section 4.2 states: \"Field names are case-insensitive.\"\n        this.name = name.toLowerCase();\n        this.type = type;\n        this.casts = casts;\n    }\n\n    public String getType() {\n        return type;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public EnumSet<Casts> getCasts() {\n        return casts;\n    }\n\n    public TokenOutputField deprecateFor(String deprecatedFor) {\n        deprecated = deprecatedFor;\n        return this;\n    }\n\n    public boolean isDeprecated() {\n        return deprecated != null;\n    }\n\n    public void wasUsed() {\n        if (deprecated != null) {\n            LOG.warn(\"------------------------------------------------------------------------\");\n            LOG.warn(\"The field \\\"{}:{}\\\" is deprecated. Use \\\"{}\\\" instead.\", type, name, deprecated);\n            LOG.warn(\"------------------------------------------------------------------------\");\n        }\n    }\n\n    @Override\n    public String toString() {\n        String msg = \"{ \"+getType()+':'+getName()+\" --> \"+casts+\" }\";\n        if (deprecated != null) {\n            return \"DEPRECATED: \" + msg;\n        }\n        return msg;\n    }\n}\n\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/main/java/nl/basjes/parse/httpdlog/dissectors/tokenformat/TokenParser.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.httpdlog.dissectors.tokenformat;\n\nimport nl.basjes.parse.core.Casts;\nimport nl.basjes.parse.core.Dissector;\nimport org.apache.commons.lang3.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.EnumSet;\nimport java.util.List;\n\npublic class TokenParser implements Serializable {\n\n    private static final Logger LOG = LoggerFactory.getLogger(TokenParser.class);\n\n    // ---------------------------------------\n    public static final String FORMAT_DIGIT = \"[0-9]\";\n    public static final String FORMAT_NUMBER = FORMAT_DIGIT + \"+\";\n    public static final String FORMAT_CLF_NUMBER = FORMAT_NUMBER + \"|-\";\n    public static final String FORMAT_HEXDIGIT = \"[0-9a-fA-F]\";\n    public static final String FORMAT_HEXNUMBER = FORMAT_HEXDIGIT + \"+\";\n    public static final String FORMAT_CLF_HEXNUMBER = FORMAT_HEXNUMBER + \"|-\";\n    public static final String FORMAT_NON_ZERO_NUMBER = \"[1-9][0-9]*\";\n    public static final String FORMAT_CLF_NON_ZERO_NUMBER = FORMAT_NON_ZERO_NUMBER + \"|-\";\n    public static final String FORMAT_EIGHT_BIT_DECIMAL = \"(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\";\n\n    // This next regex only allows for the common form of an IPv4 address that appear in a logfile.\n    public static final String FORMAT_IPV4 = \"(?:\" + FORMAT_EIGHT_BIT_DECIMAL + \"\\\\.){3}\" + FORMAT_EIGHT_BIT_DECIMAL;\n\n    // From http://codepad.org/wo95tiyp\n    public static final String FORMAT_IPV6 = \":?(?:\" + FORMAT_HEXDIGIT + \"{1,4}(?::|.)?){0,8}(?::|::)?(?:\" + FORMAT_HEXDIGIT + \"{1,4}(?::|.)?){0,8}\";\n    public static final String FORMAT_IP = FORMAT_IPV4 + \"|\" + FORMAT_IPV6;\n\n    public static final String FORMAT_CLF_IP = FORMAT_IP + \"|-\";\n    public static final String FORMAT_STRING = \".*?\";\n    public static final String FORMAT_NO_SPACE_STRING = \"[^\\\\s]*\";\n    public static final String FIXED_STRING = \"FIXED_STRING\";\n\n    // This expression \"forces\" a year in the range [1000-9999] :).\n    public static final String FORMAT_STANDARD_TIME_US =\n            \"[0-3][0-9]/(?:[a-zA-Z][a-zA-Z][a-zA-Z])/[1-9][0-9][0-9][0-9]:[0-9][0-9]:[0-9][0-9]:[0-9][0-9] [\\\\+|\\\\-][0-9][0-9][0-9][0-9]\";\n\n    public static final String FORMAT_STANDARD_TIME_ISO8601 =\n        \"[1-9][0-9][0-9][0-9]-[0-1][0-9]-[0-3][0-9]T[0-9][0-9]:[0-9][0-9]:[0-9][0-9][\\\\+|\\\\-][0-9][0-9]:[0-9][0-9]\";\n\n    public static final String FORMAT_NUMBER_DECIMAL = FORMAT_NUMBER + \"\\\\.\" + FORMAT_NUMBER;\n    public static final String FORMAT_NUMBER_OPTIONAL_DECIMAL = FORMAT_NUMBER + \"(?:\\\\.\" + FORMAT_NUMBER + \")?\";\n\n    // ---------------------------------------\n\n    private final String logFormatToken;\n    private final List<TokenOutputField> outputFields = new ArrayList<>();\n    private final String regex;\n    private final int prio;\n    protected String warningMessageWhenUsed;\n    private final Dissector customDissector;\n\n    // --------------------------------------------\n    public TokenParser(final String nLogFormatToken,\n            final String nValueName,\n            final String nValueType,\n            final EnumSet<Casts> nCasts,\n            final String nRegex) {\n        this(nLogFormatToken, nValueName, nValueType, nCasts, nRegex, 10);\n    }\n\n    public TokenParser(final String nLogFormatToken,\n                       final String nValueName,\n                       final String nValueType,\n                       final EnumSet<Casts> nCasts,\n                       final String nRegex,\n                       final int nPrio) {\n        this(nLogFormatToken, nValueName, nValueType, nCasts, nRegex, nPrio, null);\n    }\n\n    public TokenParser(final String nLogFormatToken,\n            final String nValueName,\n            final String nValueType,\n            final EnumSet<Casts> nCasts,\n            final String nRegex,\n            final int nPrio,\n           final Dissector nCustomDissector) {\n        logFormatToken = nLogFormatToken;\n        regex = nRegex;\n        prio = nPrio;\n        customDissector = nCustomDissector;\n        addOutputField(nValueType, nValueName, nCasts);\n    }\n\n    public TokenParser(final String nLogFormatToken,\n                       final String nRegex) {\n        this(nLogFormatToken, nRegex, 0, null);\n    }\n\n    public TokenParser(final String nLogFormatToken,\n                       final String nRegex,\n                       final int nPrio) {\n        this(nLogFormatToken, nRegex, nPrio, null);\n    }\n\n    public TokenParser(final String nLogFormatToken,\n                       final String nRegex,\n                       final int nPrio,\n                       final Dissector nCustomDissector) {\n        logFormatToken = nLogFormatToken;\n        regex = nRegex;\n        prio = nPrio;\n        customDissector = nCustomDissector;\n    }\n\n    public TokenParser addOutputField(String type, String name, EnumSet<Casts> casts) {\n        outputFields.add(new TokenOutputField(type, name, casts));\n        return this;\n    }\n\n    public TokenParser addOutputField(String type, String name, EnumSet<Casts> casts, String deprecateFor) {\n        outputFields.add(new TokenOutputField(type, name, casts).deprecateFor(deprecateFor));\n        return this;\n    }\n\n    public TokenParser addOutputField(TokenOutputField outputField) {\n        this.outputFields.add(outputField);\n        return this;\n    }\n\n    public TokenParser addOutputFields(List<TokenOutputField> nOutputFields) {\n        this.outputFields.addAll(nOutputFields);\n        return this;\n    }\n\n    public List<TokenOutputField> getOutputFields() {\n        return outputFields;\n    }\n\n    // --------------------------------------------\n\n    public TokenParser setWarningMessageWhenUsed(String message) {\n        this.warningMessageWhenUsed = message;\n        return this;\n    }\n\n    public String getLogFormatToken() {\n        return logFormatToken;\n    }\n\n    public String getRegex() {\n        return regex;\n    }\n\n    public int getPrio() {\n        return prio;\n    }\n\n    public Dissector getCustomDissector() {\n        return customDissector;\n    }\n\n    // --------------------------------------------\n\n    public Token getNextToken(final String logFormat, final int startOffset) {\n        final int pos = logFormat.indexOf(logFormatToken, startOffset);\n        if (pos == -1) {\n            return null;\n        }\n\n        Token token = new Token(\n                regex,\n                pos,\n                logFormatToken.length(),\n                prio)\n            .addOutputFields(outputFields);\n\n        if (warningMessageWhenUsed != null) {\n            token.setWarningMessageWhenUsed(warningMessageWhenUsed);\n        }\n\n        if (!addCustomDissector(token,\n            outputFields.get(0).getType(),\n            outputFields.get(0).getName())) {\n            return null;\n        }\n\n        return token;\n    }\n\n    // --------------------------------------------\n\n    public List<Token> getTokens(final String logFormat) {\n        if (StringUtils.isBlank(logFormat)) {\n            return null;\n        }\n\n        final List<Token> result = new ArrayList<>(5); // Usually a good guess\n\n        int offset = 0;\n        while (true) {\n            final Token token = getNextToken(logFormat, offset);\n            if (token == null) {\n                break;\n            }\n            result.add(token);\n            offset = token.getStartPos() + token.getLength();\n        }\n        return result;\n    }\n\n    // --------------------------------------------\n\n    protected boolean addCustomDissector(Token token, String fieldType, String fieldName) {\n        if (customDissector == null) {\n            return true;\n        }\n        try {\n            Dissector dissector = customDissector.getNewInstance();\n            dissector.setInputType(fieldType);\n            if (!dissector.initializeFromSettingsParameter(fieldName)) {\n                LOG.error(\"Unable to INITIALIZE custom dissector for {}:{}\", fieldType, fieldName);\n                return false;\n            }\n            token.setCustomDissector(dissector);\n        } catch (Exception e) {\n            LOG.error(\"Unable to add custom dissector for {}:{} because of : {}\", fieldType, fieldName, e.getMessage());\n            return false;\n        }\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/main/java/nl/basjes/parse/httpdlog/dissectors/tokenformat/TokenSorterByStartPos.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.httpdlog.dissectors.tokenformat;\n\nimport java.io.Serializable;\nimport java.util.Comparator;\n\npublic class TokenSorterByStartPos implements Comparator<Token>, Serializable {\n    @Override\n    public int compare(final Token t1, final Token t2) {\n\n        int startPosDiff = Integer.compare(t1.getStartPos(), t2.getStartPos());\n        if (startPosDiff != 0) {\n            return startPosDiff;\n        }\n        int lengthDiff = Integer.compare(t1.getLength(), t2.getLength());\n        if (lengthDiff != 0) {\n            return lengthDiff;\n        }\n\n        return -1 * Integer.compare(t1.getPrio(), t2.getPrio());\n\n    }\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/main/java/nl/basjes/parse/httpdlog/dissectors/translate/ConvertCLFIntoNumber.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.httpdlog.dissectors.translate;\n\nimport nl.basjes.parse.core.Parsable;\nimport nl.basjes.parse.core.Value;\nimport nl.basjes.parse.core.exceptions.DissectionFailure;\n\npublic class ConvertCLFIntoNumber extends TypeConvertBaseDissector {\n    public ConvertCLFIntoNumber() {\n        super();\n    }\n\n    public ConvertCLFIntoNumber(String nInputType, String nOutputType) {\n        super(nInputType, nOutputType);\n    }\n\n    @Override\n    public void dissect(Parsable<?> parsable, String inputname, Value value) throws DissectionFailure {\n        String stringValue = value.getString();\n        if (stringValue == null || \"-\".equals(stringValue)) {\n            parsable.addDissection(inputname, outputType, \"\", 0);\n        } else {\n            parsable.addDissection(inputname, outputType, \"\", value);\n        }\n    }\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/main/java/nl/basjes/parse/httpdlog/dissectors/translate/ConvertMillisecondsIntoMicroseconds.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.httpdlog.dissectors.translate;\n\nimport nl.basjes.parse.core.Parsable;\nimport nl.basjes.parse.core.Value;\nimport nl.basjes.parse.core.exceptions.DissectionFailure;\n\npublic class ConvertMillisecondsIntoMicroseconds extends TypeConvertBaseDissector {\n    public ConvertMillisecondsIntoMicroseconds() {\n        super();\n    }\n\n    public ConvertMillisecondsIntoMicroseconds(String inputType, String nOutputType) {\n        super(inputType, nOutputType);\n    }\n\n    @Override\n    public void dissect(Parsable<?> parsable, String inputname, Value value) throws DissectionFailure {\n        parsable.addDissection(inputname, outputType, \"\", value.getLong() * 1000);\n    }\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/main/java/nl/basjes/parse/httpdlog/dissectors/translate/ConvertNumberIntoCLF.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.httpdlog.dissectors.translate;\n\nimport nl.basjes.parse.core.Parsable;\nimport nl.basjes.parse.core.Value;\nimport nl.basjes.parse.core.exceptions.DissectionFailure;\n\npublic class ConvertNumberIntoCLF extends TypeConvertBaseDissector {\n    public ConvertNumberIntoCLF() {\n        super();\n    }\n\n    public ConvertNumberIntoCLF(String inputType, String nOutputType) {\n        super(inputType, nOutputType);\n    }\n\n    @Override\n    public void dissect(Parsable<?> parsable, String inputname, Value value) throws DissectionFailure {\n        parsable.addDissection(inputname, outputType, \"\", value);\n    }\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/main/java/nl/basjes/parse/httpdlog/dissectors/translate/ConvertSecondsWithMillisStringDissector.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.httpdlog.dissectors.translate;\n\nimport nl.basjes.parse.core.Parsable;\nimport nl.basjes.parse.core.Value;\nimport nl.basjes.parse.core.exceptions.DissectionFailure;\n\npublic class ConvertSecondsWithMillisStringDissector extends TypeConvertBaseDissector {\n    public ConvertSecondsWithMillisStringDissector() {\n        super();\n    }\n\n    public ConvertSecondsWithMillisStringDissector(String inputType, String nOutputType) {\n        super(inputType, nOutputType);\n    }\n\n    @Override\n    public void dissect(Parsable<?> parsable, String inputname, Value value) throws DissectionFailure {\n        String[] epochStrings = value.getString().split(\"\\\\.\", 2);\n        Long seconds =  Long.parseLong(epochStrings[0]);\n        Long milliseconds =  Long.parseLong(epochStrings[1]);\n        Long epoch = seconds * 1000 + milliseconds;\n\n        parsable.addDissection(inputname, outputType, \"\", epoch);\n    }\n}\n\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/main/java/nl/basjes/parse/httpdlog/dissectors/translate/TypeConvertBaseDissector.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.httpdlog.dissectors.translate;\n\nimport nl.basjes.parse.core.Casts;\nimport nl.basjes.parse.core.Dissector;\nimport nl.basjes.parse.core.SimpleDissector;\nimport nl.basjes.parse.core.exceptions.InvalidDissectorException;\n\nimport java.util.EnumSet;\nimport java.util.HashMap;\n\nimport static nl.basjes.parse.core.Casts.STRING_OR_LONG;\n\npublic abstract class TypeConvertBaseDissector extends SimpleDissector {\n    protected String inputType;\n    protected String outputType;\n    public TypeConvertBaseDissector() {\n        super(null, new HashMap<>());\n    }\n\n    private static HashMap<String, EnumSet<Casts>> fillOutputConfig(String outputType, EnumSet<Casts> casts) {\n        HashMap<String, EnumSet<Casts>>  typeConvertConfig = new HashMap<>();\n        typeConvertConfig.put(outputType + \":\", casts);\n        return typeConvertConfig;\n    }\n\n    public TypeConvertBaseDissector(String nInputType, String nOutputType) {\n        super(nInputType, fillOutputConfig(nOutputType, STRING_OR_LONG));\n        inputType = nInputType;\n        outputType = nOutputType;\n    }\n\n    @Override\n    protected void initializeNewInstance(Dissector newInstance) throws InvalidDissectorException {\n        super.initializeNewInstance(newInstance);\n        ((TypeConvertBaseDissector)newInstance).inputType = inputType;\n        ((TypeConvertBaseDissector)newInstance).outputType = outputType;\n    }\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/main/resources/version/Version.java.template",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.httpdlog;\n\npublic final class Version {\n\n    public static final String GIT_COMMIT_ID_DESCRIBE_SHORT  = \"@git.commit.id.describe-short@\";\n    public static final String PROJECT_BUILD_OUTPUTTIMESTAMP = \"@project.build.outputTimestamp@\";\n    public static final String PROJECT_VERSION               = \"@project.version@\";\n\n    public static String getGitCommitIdDescribeShort() {\n        return GIT_COMMIT_ID_DESCRIBE_SHORT;\n    }\n\n    public static String getBuildTimestamp() {\n        return PROJECT_BUILD_OUTPUTTIMESTAMP;\n    }\n\n    public static String getProjectVersion() {\n        return PROJECT_VERSION;\n    }\n\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/test/java/nl/basjes/parse/httpdlog/ApacheHttpdAllFieldsTest.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage nl.basjes.parse.httpdlog;\n\nimport nl.basjes.parse.core.test.DissectorTester;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.List;\n\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\nclass ApacheHttpdAllFieldsTest {\n\n    private void verifyFieldAvailability(String logformat, String... expectedFields) {\n\n        List<String> possible = DissectorTester.create()\n            .withDissector(new HttpdLogFormatDissector(logformat))\n            .getPossible();\n\n        for (String expectedField:expectedFields) {\n            assertTrue(possible.contains(expectedField),\n                \"Logformat >>>\" + logformat + \"<<< should produce \" + expectedField + \" instead we found: \" + possible);\n        }\n    }\n\n    @Test\n    void checkDeprecationMessage() {\n        DissectorTester.create()\n            .withDissector(new HttpdLogFormatDissector(\"%b %D Deprecated\"))\n            .withInput(\"1 2 Deprecated\")\n            .withInput(\"1 2 Deprecated\")\n            .withInput(\"1 2 Deprecated\")\n            .withInput(\"1 2 Deprecated\")\n            .withInput(\"1 2 Deprecated\")\n            .withInput(\"1 2 Deprecated\")\n            .withInput(\"1 2 Deprecated\")\n            .withInput(\"1 2 Deprecated\")\n            .withInput(\"1 2 Deprecated\")\n            .withInput(\"1 2 Deprecated\")\n            .expect(\"BYTES:response.body.bytesclf\",       \"1\")\n            .expect(\"MICROSECONDS:server.process.time\",   \"2\")\n            .checkExpectations();\n    }\n\n    @Test\n    void testAllFieldsAvailability() {\n        verifyFieldAvailability(\"%a\",                   \"IP:connection.client.ip\",\n                                                        \"IP:connection.client.ip.last\");\n        verifyFieldAvailability(\"%<a\",                  \"IP:connection.client.ip.original\");\n        verifyFieldAvailability(\"%>a\",                  \"IP:connection.client.ip.last\");\n\n        verifyFieldAvailability(\"%{c}a\",                \"IP:connection.client.peerip\",\n                                                        \"IP:connection.client.peerip.last\");\n        verifyFieldAvailability(\"%<{c}a\",               \"IP:connection.client.peerip.original\");\n        verifyFieldAvailability(\"%>{c}a\",               \"IP:connection.client.peerip.last\");\n\n        verifyFieldAvailability(\"%A\",                   \"IP:connection.server.ip\",\n                                                        \"IP:connection.server.ip.last\");\n        verifyFieldAvailability(\"%<A\",                  \"IP:connection.server.ip.original\");\n        verifyFieldAvailability(\"%>A\",                  \"IP:connection.server.ip.last\");\n\n        verifyFieldAvailability(\"%B\",                   \"BYTES:response.body.bytes\",\n                                                        \"BYTES:response.body.bytes.last\");\n        verifyFieldAvailability(\"%<B\",                  \"BYTES:response.body.bytes.original\");\n        verifyFieldAvailability(\"%>B\",                  \"BYTES:response.body.bytes.last\");\n\n        verifyFieldAvailability(\"%b Deprecated\",        \"BYTES:response.body.bytesclf\");\n        verifyFieldAvailability(\"%b\",                   \"BYTESCLF:response.body.bytes\",\n                                                        \"BYTESCLF:response.body.bytes.last\");\n        verifyFieldAvailability(\"%<b\",                  \"BYTESCLF:response.body.bytes.original\");\n        verifyFieldAvailability(\"%>b\",                  \"BYTESCLF:response.body.bytes.last\");\n\n        verifyFieldAvailability(\"%{FooBar}C\",           \"HTTP.COOKIE:request.cookies.foobar\");\n        verifyFieldAvailability(\"%{FooBar}e\",           \"VARIABLE:server.environment.foobar\");\n\n        verifyFieldAvailability(\"%f\",                   \"FILENAME:server.filename\",\n                                                        \"FILENAME:server.filename.last\");\n        verifyFieldAvailability(\"%<f\",                  \"FILENAME:server.filename.original\");\n        verifyFieldAvailability(\"%>f\",                  \"FILENAME:server.filename.last\");\n\n        verifyFieldAvailability(\"%h\",                   \"IP:connection.client.host\",\n                                                        \"IP:connection.client.host.last\");\n        verifyFieldAvailability(\"%<h\",                  \"IP:connection.client.host.original\");\n        verifyFieldAvailability(\"%>h\",                  \"IP:connection.client.host.last\");\n\n        verifyFieldAvailability(\"%H\",                   \"PROTOCOL:request.protocol\",\n                                                        \"PROTOCOL:request.protocol.last\");\n        verifyFieldAvailability(\"%<H\",                  \"PROTOCOL:request.protocol.original\");\n        verifyFieldAvailability(\"%>H\",                  \"PROTOCOL:request.protocol.last\");\n\n        verifyFieldAvailability(\"%{FooBar}i\",           \"HTTP.HEADER:request.header.foobar\");\n        verifyFieldAvailability(\"%{FooBar}^ti\",         \"HTTP.TRAILER:request.trailer.foobar\");\n\n        verifyFieldAvailability(\"%k\",                   \"NUMBER:connection.keepalivecount\",\n                                                        \"NUMBER:connection.keepalivecount.last\");\n        verifyFieldAvailability(\"%<k\",                  \"NUMBER:connection.keepalivecount.original\");\n        verifyFieldAvailability(\"%>k\",                  \"NUMBER:connection.keepalivecount.last\");\n\n        verifyFieldAvailability(\"%l\",                   \"NUMBER:connection.client.logname\",\n                                                        \"NUMBER:connection.client.logname.last\");\n        verifyFieldAvailability(\"%<l\",                  \"NUMBER:connection.client.logname.original\");\n        verifyFieldAvailability(\"%>l\",                  \"NUMBER:connection.client.logname.last\");\n\n        verifyFieldAvailability(\"%L\",                   \"STRING:request.errorlogid\",\n                                                        \"STRING:request.errorlogid.last\");\n        verifyFieldAvailability(\"%<L\",                  \"STRING:request.errorlogid.original\");\n        verifyFieldAvailability(\"%>L\",                  \"STRING:request.errorlogid.last\");\n\n        verifyFieldAvailability(\"%m\",                   \"HTTP.METHOD:request.method\",\n                                                        \"HTTP.METHOD:request.method.last\");\n        verifyFieldAvailability(\"%<m\",                  \"HTTP.METHOD:request.method.original\");\n        verifyFieldAvailability(\"%>m\",                  \"HTTP.METHOD:request.method.last\");\n\n        verifyFieldAvailability(\"%{FooBar}n\",           \"STRING:server.module_note.foobar\");\n        verifyFieldAvailability(\"%{FooBar}o\",           \"HTTP.HEADER:response.header.foobar\");\n        verifyFieldAvailability(\"%{FooBar}^to\",         \"HTTP.TRAILER:response.trailer.foobar\");\n\n        verifyFieldAvailability(\"%p\",                   \"PORT:request.server.port.canonical\",\n                                                        \"PORT:request.server.port.canonical.last\");\n        verifyFieldAvailability(\"%<p\",                  \"PORT:request.server.port.canonical.original\");\n        verifyFieldAvailability(\"%>p\",                  \"PORT:request.server.port.canonical.last\");\n\n        verifyFieldAvailability(\"%{canonical}p\",        \"PORT:connection.server.port.canonical\",\n                                                        \"PORT:connection.server.port.canonical.last\");\n        verifyFieldAvailability(\"%<{canonical}p\",       \"PORT:connection.server.port.canonical.original\");\n        verifyFieldAvailability(\"%>{canonical}p\",       \"PORT:connection.server.port.canonical.last\");\n\n        verifyFieldAvailability(\"%{local}p\",            \"PORT:connection.server.port\",\n                                                        \"PORT:connection.server.port.last\");\n        verifyFieldAvailability(\"%<{local}p\",           \"PORT:connection.server.port.original\");\n        verifyFieldAvailability(\"%>{local}p\",           \"PORT:connection.server.port.last\");\n\n        verifyFieldAvailability(\"%{remote}p\",           \"PORT:connection.client.port\",\n                                                        \"PORT:connection.client.port.last\");\n        verifyFieldAvailability(\"%<{remote}p\",          \"PORT:connection.client.port.original\");\n        verifyFieldAvailability(\"%>{remote}p\",          \"PORT:connection.client.port.last\");\n\n        verifyFieldAvailability(\"%P\",                   \"NUMBER:connection.server.child.processid\",\n                                                        \"NUMBER:connection.server.child.processid.last\");\n        verifyFieldAvailability(\"%<P\",                  \"NUMBER:connection.server.child.processid.original\");\n        verifyFieldAvailability(\"%>P\",                  \"NUMBER:connection.server.child.processid.last\");\n\n        verifyFieldAvailability(\"%{pid}P\",              \"NUMBER:connection.server.child.processid\",\n                                                        \"NUMBER:connection.server.child.processid.last\");\n        verifyFieldAvailability(\"%<{pid}P\",             \"NUMBER:connection.server.child.processid.original\");\n        verifyFieldAvailability(\"%>{pid}P\",             \"NUMBER:connection.server.child.processid.last\");\n\n        verifyFieldAvailability(\"%{tid}P\",              \"NUMBER:connection.server.child.threadid\",\n                                                        \"NUMBER:connection.server.child.threadid.last\");\n        verifyFieldAvailability(\"%<{tid}P\",             \"NUMBER:connection.server.child.threadid.original\");\n        verifyFieldAvailability(\"%>{tid}P\",             \"NUMBER:connection.server.child.threadid.last\");\n\n        verifyFieldAvailability(\"%{hextid}P\",           \"NUMBER:connection.server.child.hexthreadid\",\n                                                        \"NUMBER:connection.server.child.hexthreadid.last\");\n        verifyFieldAvailability(\"%<{hextid}P\",          \"NUMBER:connection.server.child.hexthreadid.original\");\n        verifyFieldAvailability(\"%>{hextid}P\",          \"NUMBER:connection.server.child.hexthreadid.last\");\n\n        verifyFieldAvailability(\"%q\",                   \"HTTP.QUERYSTRING:request.querystring\",\n                                                        \"HTTP.QUERYSTRING:request.querystring.last\");\n        verifyFieldAvailability(\"%<q\",                  \"HTTP.QUERYSTRING:request.querystring.original\");\n        verifyFieldAvailability(\"%>q\",                  \"HTTP.QUERYSTRING:request.querystring.last\");\n\n        verifyFieldAvailability(\"%r\",                   \"HTTP.FIRSTLINE:request.firstline\",\n                                                        \"HTTP.FIRSTLINE:request.firstline.original\");\n        verifyFieldAvailability(\"%<r\",                  \"HTTP.FIRSTLINE:request.firstline.original\");\n        verifyFieldAvailability(\"%>r\",                  \"HTTP.FIRSTLINE:request.firstline.last\");\n\n        verifyFieldAvailability(\"%R\",                   \"STRING:request.handler\",\n                                                        \"STRING:request.handler.last\");\n        verifyFieldAvailability(\"%<R\",                  \"STRING:request.handler.original\");\n        verifyFieldAvailability(\"%>R\",                  \"STRING:request.handler.last\");\n\n        verifyFieldAvailability(\"%s\",                   \"STRING:request.status\",\n                                                        \"STRING:request.status.original\");\n        verifyFieldAvailability(\"%<s\",                  \"STRING:request.status.original\");\n        verifyFieldAvailability(\"%>s\",                  \"STRING:request.status.last\");\n\n        verifyFieldAvailability(\"%t\",                   \"TIME.STAMP:request.receive.time\",\n                                                        \"TIME.STAMP:request.receive.time.last\");\n        verifyFieldAvailability(\"%<t\",                  \"TIME.STAMP:request.receive.time.original\");\n        verifyFieldAvailability(\"%>t\",                  \"TIME.STAMP:request.receive.time.last\");\n\n        verifyFieldAvailability(\"%{%Y}t\",               \"TIME.YEAR:request.receive.time.year\");\n        verifyFieldAvailability(\"%{begin:%Y}t\",         \"TIME.YEAR:request.receive.time.begin.year\");\n        verifyFieldAvailability(\"%{end:%Y}t\",           \"TIME.YEAR:request.receive.time.end.year\");\n\n        verifyFieldAvailability(\"%{sec}t\",              \"TIME.SECONDS:request.receive.time.sec\",\n                                                        \"TIME.SECONDS:request.receive.time.sec\");\n        verifyFieldAvailability(\"%<{sec}t\",             \"TIME.SECONDS:request.receive.time.sec.original\");\n        verifyFieldAvailability(\"%>{sec}t\",             \"TIME.SECONDS:request.receive.time.sec.last\");\n\n        verifyFieldAvailability(\"%{begin:sec}t\",        \"TIME.SECONDS:request.receive.time.begin.sec\",\n                                                        \"TIME.SECONDS:request.receive.time.begin.sec.last\");\n        verifyFieldAvailability(\"%<{begin:sec}t\",       \"TIME.SECONDS:request.receive.time.begin.sec.original\");\n        verifyFieldAvailability(\"%>{begin:sec}t\",       \"TIME.SECONDS:request.receive.time.begin.sec.last\");\n\n        verifyFieldAvailability(\"%{end:sec}t\",          \"TIME.SECONDS:request.receive.time.end.sec\",\n                                                        \"TIME.SECONDS:request.receive.time.end.sec.last\");\n        verifyFieldAvailability(\"%<{end:sec}t\",         \"TIME.SECONDS:request.receive.time.end.sec.original\");\n        verifyFieldAvailability(\"%>{end:sec}t\",         \"TIME.SECONDS:request.receive.time.end.sec.last\");\n\n        verifyFieldAvailability(\"%{msec}t Deprecated\",  \"TIME.EPOCH:request.receive.time.begin.msec\");\n        verifyFieldAvailability(\"%{msec}t\",             \"TIME.EPOCH:request.receive.time.msec\",\n                                                        \"TIME.EPOCH:request.receive.time.msec.last\");\n        verifyFieldAvailability(\"%<{msec}t\",            \"TIME.EPOCH:request.receive.time.msec.original\");\n        verifyFieldAvailability(\"%>{msec}t\",            \"TIME.EPOCH:request.receive.time.msec.last\");\n\n        verifyFieldAvailability(\"%{begin:msec}t\",       \"TIME.EPOCH:request.receive.time.begin.msec\",\n                                                        \"TIME.EPOCH:request.receive.time.begin.msec.last\");\n        verifyFieldAvailability(\"%<{begin:msec}t\",      \"TIME.EPOCH:request.receive.time.begin.msec.original\");\n        verifyFieldAvailability(\"%>{begin:msec}t\",      \"TIME.EPOCH:request.receive.time.begin.msec.last\");\n\n        verifyFieldAvailability(\"%{end:msec}t\",         \"TIME.EPOCH:request.receive.time.end.msec\",\n                                                        \"TIME.EPOCH:request.receive.time.end.msec.last\");\n        verifyFieldAvailability(\"%<{end:msec}t\",        \"TIME.EPOCH:request.receive.time.end.msec.original\");\n        verifyFieldAvailability(\"%>{end:msec}t\",        \"TIME.EPOCH:request.receive.time.end.msec.last\");\n\n        verifyFieldAvailability(\"%{usec}t Deprecated\",  \"TIME.EPOCH.USEC:request.receive.time.begin.usec\");\n        verifyFieldAvailability(\"%{usec}t\",             \"TIME.EPOCH.USEC:request.receive.time.usec\",\n                                                        \"TIME.EPOCH.USEC:request.receive.time.usec.last\");\n        verifyFieldAvailability(\"%<{usec}t\",            \"TIME.EPOCH.USEC:request.receive.time.usec.original\");\n        verifyFieldAvailability(\"%>{usec}t\",            \"TIME.EPOCH.USEC:request.receive.time.usec.last\");\n\n        verifyFieldAvailability(\"%{begin:usec}t\",       \"TIME.EPOCH.USEC:request.receive.time.begin.usec\",\n                                                        \"TIME.EPOCH.USEC:request.receive.time.begin.usec.last\");\n        verifyFieldAvailability(\"%<{begin:usec}t\",      \"TIME.EPOCH.USEC:request.receive.time.begin.usec.original\");\n        verifyFieldAvailability(\"%>{begin:usec}t\",      \"TIME.EPOCH.USEC:request.receive.time.begin.usec.last\");\n\n        verifyFieldAvailability(\"%{end:usec}t\",         \"TIME.EPOCH.USEC:request.receive.time.end.usec\",\n                                                        \"TIME.EPOCH.USEC:request.receive.time.end.usec.last\");\n        verifyFieldAvailability(\"%<{end:usec}t\",        \"TIME.EPOCH.USEC:request.receive.time.end.usec.original\");\n        verifyFieldAvailability(\"%>{end:usec}t\",        \"TIME.EPOCH.USEC:request.receive.time.end.usec.last\");\n\n        verifyFieldAvailability(\"%{msec_frac}t Deprecated\", \"TIME.EPOCH:request.receive.time.begin.msec_frac\");\n        verifyFieldAvailability(\"%{msec_frac}t\",        \"TIME.EPOCH:request.receive.time.msec_frac\",\n                                                        \"TIME.EPOCH:request.receive.time.msec_frac.last\");\n        verifyFieldAvailability(\"%<{msec_frac}t\",       \"TIME.EPOCH:request.receive.time.msec_frac.original\");\n        verifyFieldAvailability(\"%>{msec_frac}t\",       \"TIME.EPOCH:request.receive.time.msec_frac.last\");\n\n        verifyFieldAvailability(\"%{begin:msec_frac}t\",  \"TIME.EPOCH:request.receive.time.begin.msec_frac\",\n                                                        \"TIME.EPOCH:request.receive.time.begin.msec_frac.last\");\n        verifyFieldAvailability(\"%<{begin:msec_frac}t\", \"TIME.EPOCH:request.receive.time.begin.msec_frac.original\");\n        verifyFieldAvailability(\"%>{begin:msec_frac}t\", \"TIME.EPOCH:request.receive.time.begin.msec_frac.last\");\n\n        verifyFieldAvailability(\"%{end:msec_frac}t\",    \"TIME.EPOCH:request.receive.time.end.msec_frac\",\n                                                        \"TIME.EPOCH:request.receive.time.end.msec_frac.last\");\n        verifyFieldAvailability(\"%<{end:msec_frac}t\",   \"TIME.EPOCH:request.receive.time.end.msec_frac.original\");\n        verifyFieldAvailability(\"%>{end:msec_frac}t\",   \"TIME.EPOCH:request.receive.time.end.msec_frac.last\");\n\n        verifyFieldAvailability(\"%{usec_frac}t Deprecated\", \"TIME.EPOCH.USEC_FRAC:request.receive.time.begin.usec_frac\");\n        verifyFieldAvailability(\"%{usec_frac}t\",        \"TIME.EPOCH.USEC_FRAC:request.receive.time.usec_frac\",\n                                                        \"TIME.EPOCH.USEC_FRAC:request.receive.time.usec_frac.last\");\n        verifyFieldAvailability(\"%<{usec_frac}t\",       \"TIME.EPOCH.USEC_FRAC:request.receive.time.usec_frac.original\");\n        verifyFieldAvailability(\"%>{usec_frac}t\",       \"TIME.EPOCH.USEC_FRAC:request.receive.time.usec_frac.last\");\n\n        verifyFieldAvailability(\"%{begin:usec_frac}t\",  \"TIME.EPOCH.USEC_FRAC:request.receive.time.begin.usec_frac\",\n                                                        \"TIME.EPOCH.USEC_FRAC:request.receive.time.begin.usec_frac.last\");\n        verifyFieldAvailability(\"%<{begin:usec_frac}t\", \"TIME.EPOCH.USEC_FRAC:request.receive.time.begin.usec_frac.original\");\n        verifyFieldAvailability(\"%>{begin:usec_frac}t\", \"TIME.EPOCH.USEC_FRAC:request.receive.time.begin.usec_frac.last\");\n\n        verifyFieldAvailability(\"%{end:usec_frac}t\",    \"TIME.EPOCH.USEC_FRAC:request.receive.time.end.usec_frac\",\n                                                        \"TIME.EPOCH.USEC_FRAC:request.receive.time.end.usec_frac.last\");\n        verifyFieldAvailability(\"%<{end:usec_frac}t\",   \"TIME.EPOCH.USEC_FRAC:request.receive.time.end.usec_frac.original\");\n        verifyFieldAvailability(\"%>{end:usec_frac}t\",   \"TIME.EPOCH.USEC_FRAC:request.receive.time.end.usec_frac.last\");\n\n        verifyFieldAvailability(\"%T\",                   \"SECONDS:response.server.processing.time\",\n                                                        \"SECONDS:response.server.processing.time.original\");\n        verifyFieldAvailability(\"%<T\",                  \"SECONDS:response.server.processing.time.original\");\n        verifyFieldAvailability(\"%>T\",                  \"SECONDS:response.server.processing.time.last\");\n\n        verifyFieldAvailability(\"%D Deprecated\",        \"MICROSECONDS:server.process.time\");\n        verifyFieldAvailability(\"%D\",                   \"MICROSECONDS:response.server.processing.time\",\n                                                        \"MICROSECONDS:response.server.processing.time.original\");\n        verifyFieldAvailability(\"%<D\",                  \"MICROSECONDS:response.server.processing.time.original\");\n        verifyFieldAvailability(\"%>D\",                  \"MICROSECONDS:response.server.processing.time.last\");\n\n        verifyFieldAvailability(\"%{us}T\",               \"MICROSECONDS:response.server.processing.time\",\n                                                        \"MICROSECONDS:response.server.processing.time.original\");\n        verifyFieldAvailability(\"%<{us}T\",              \"MICROSECONDS:response.server.processing.time.original\");\n        verifyFieldAvailability(\"%>{us}T\",              \"MICROSECONDS:response.server.processing.time.last\");\n\n        verifyFieldAvailability(\"%{ms}T\",               \"MILLISECONDS:response.server.processing.time\",\n                                                        \"MILLISECONDS:response.server.processing.time.original\");\n        verifyFieldAvailability(\"%<{ms}T\",              \"MILLISECONDS:response.server.processing.time.original\");\n        verifyFieldAvailability(\"%>{ms}T\",              \"MILLISECONDS:response.server.processing.time.last\");\n\n        verifyFieldAvailability(\"%{s}T\",                \"SECONDS:response.server.processing.time\",\n                                                        \"SECONDS:response.server.processing.time.original\");\n        verifyFieldAvailability(\"%<{s}T\",               \"SECONDS:response.server.processing.time.original\");\n        verifyFieldAvailability(\"%>{s}T\",               \"SECONDS:response.server.processing.time.last\");\n\n        verifyFieldAvailability(\"%u\",                   \"STRING:connection.client.user\",\n                                                        \"STRING:connection.client.user.last\");\n        verifyFieldAvailability(\"%<u\",                  \"STRING:connection.client.user.original\");\n        verifyFieldAvailability(\"%>u\",                  \"STRING:connection.client.user.last\");\n\n        verifyFieldAvailability(\"%U\",                   \"URI:request.urlpath\",\n                                                        \"URI:request.urlpath.original\");\n        verifyFieldAvailability(\"%<U\",                  \"URI:request.urlpath.original\");\n        verifyFieldAvailability(\"%>U\",                  \"URI:request.urlpath.last\");\n\n        verifyFieldAvailability(\"%v\",                   \"STRING:connection.server.name.canonical\",\n                                                        \"STRING:connection.server.name.canonical.last\");\n        verifyFieldAvailability(\"%<v\",                  \"STRING:connection.server.name.canonical.original\");\n        verifyFieldAvailability(\"%>v\",                  \"STRING:connection.server.name.canonical.last\");\n\n        verifyFieldAvailability(\"%V\",                   \"STRING:connection.server.name\",\n                                                        \"STRING:connection.server.name.last\");\n        verifyFieldAvailability(\"%<V\",                  \"STRING:connection.server.name.original\");\n        verifyFieldAvailability(\"%>V\",                  \"STRING:connection.server.name.last\");\n\n        verifyFieldAvailability(\"%X\",                   \"HTTP.CONNECTSTATUS:response.connection.status\",\n                                                        \"HTTP.CONNECTSTATUS:response.connection.status.last\");\n        verifyFieldAvailability(\"%<X\",                  \"HTTP.CONNECTSTATUS:response.connection.status.original\");\n        verifyFieldAvailability(\"%>X\",                  \"HTTP.CONNECTSTATUS:response.connection.status.last\");\n\n        verifyFieldAvailability(\"%I\",                   \"BYTES:request.bytes\",\n                                                        \"BYTES:request.bytes.last\");\n        verifyFieldAvailability(\"%<I\",                  \"BYTES:request.bytes.original\");\n        verifyFieldAvailability(\"%>I\",                  \"BYTES:request.bytes.last\");\n\n        verifyFieldAvailability(\"%O\",                   \"BYTES:response.bytes\",\n                                                        \"BYTES:response.bytes.last\");\n        verifyFieldAvailability(\"%<O\",                  \"BYTES:response.bytes.original\");\n        verifyFieldAvailability(\"%>O\",                  \"BYTES:response.bytes.last\");\n\n        verifyFieldAvailability(\"%S\",                   \"BYTES:total.bytes\",\n                                                        \"BYTES:total.bytes.last\");\n        verifyFieldAvailability(\"%<S\",                  \"BYTES:total.bytes.original\");\n        verifyFieldAvailability(\"%>S\",                  \"BYTES:total.bytes.last\");\n\n        verifyFieldAvailability(\"%{cookie}i\",           \"HTTP.COOKIES:request.cookies\",\n                                                        \"HTTP.COOKIES:request.cookies.last\");\n        verifyFieldAvailability(\"%<{cookie}i\",          \"HTTP.COOKIES:request.cookies.original\");\n        verifyFieldAvailability(\"%>{cookie}i\",          \"HTTP.COOKIES:request.cookies.last\");\n\n        verifyFieldAvailability(\"%{set-cookie}o\",       \"HTTP.SETCOOKIES:response.cookies\",\n                                                        \"HTTP.SETCOOKIES:response.cookies.last\");\n        verifyFieldAvailability(\"%<{set-cookie}o\",      \"HTTP.SETCOOKIES:response.cookies.original\");\n        verifyFieldAvailability(\"%>{set-cookie}o\",      \"HTTP.SETCOOKIES:response.cookies.last\");\n\n        verifyFieldAvailability(\"%{user-agent}i\",       \"HTTP.USERAGENT:request.user-agent\",\n                                                        \"HTTP.USERAGENT:request.user-agent.last\");\n        verifyFieldAvailability(\"%<{user-agent}i\",      \"HTTP.USERAGENT:request.user-agent.original\");\n        verifyFieldAvailability(\"%>{user-agent}i\",      \"HTTP.USERAGENT:request.user-agent.last\");\n\n        verifyFieldAvailability(\"%{referer}i\",          \"HTTP.URI:request.referer\",\n                                                        \"HTTP.URI:request.referer.last\");\n        verifyFieldAvailability(\"%<{referer}i\",         \"HTTP.URI:request.referer.original\");\n        verifyFieldAvailability(\"%>{referer}i\",         \"HTTP.URI:request.referer.last\");\n    }\n\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/test/java/nl/basjes/parse/httpdlog/ApacheHttpdLogParserTest.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage nl.basjes.parse.httpdlog;\n\nimport nl.basjes.parse.core.Field;\nimport nl.basjes.parse.core.Parser;\nimport nl.basjes.parse.core.exceptions.MissingDissectorsException;\nimport nl.basjes.parse.core.test.DissectorTester;\nimport nl.basjes.parse.httpdlog.dissectors.ScreenResolutionDissector;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.api.Assertions.fail;\n\nclass ApacheHttpdLogParserTest {\n\n    // ------------------------------------------\n\n    public static class TestRecord {\n        private final Map<String, String> results = new HashMap<>(32);\n\n        @SuppressWarnings(\"UnusedDeclaration\")\n        @Field({\n            \"STRING:request.firstline.uri.query.*\",\n            \"STRING:request.querystring.aap\",\n            \"IP:connection.client.ip\",\n            \"NUMBER:connection.client.logname\",\n            \"STRING:connection.client.user\",\n            \"TIME.STAMP:request.receive.time\",\n            \"TIME.SECOND:request.receive.time.second\",\n            \"HTTP.URI:request.firstline.uri\",\n            \"STRING:request.status.last\",\n            \"BYTESCLF:response.body.bytes\",\n            \"HTTP.URI:request.referer\",\n            \"STRING:request.referer.query.mies\",\n            \"STRING:request.referer.query.wim\",\n            \"HTTP.USERAGENT:request.user-agent\",\n            \"TIME.DAY:request.receive.time.day\",\n            \"TIME.HOUR:request.receive.time.hour\",\n            \"TIME.MONTHNAME:request.receive.time.monthname\",\n            \"TIME.EPOCH:request.receive.time.epoch\",\n            \"TIME.WEEK:request.receive.time.weekofweekyear\",\n            \"TIME.YEAR:request.receive.time.weekyear\",\n            \"TIME.YEAR:request.receive.time.year\",\n            \"HTTP.COOKIES:request.cookies\",\n            \"HTTP.SETCOOKIES:response.cookies\",\n            \"HTTP.COOKIE:request.cookies.jquery-ui-theme\",\n            \"HTTP.SETCOOKIE:response.cookies.apache\",\n            \"STRING:response.cookies.apache.domain\",\n            \"MICROSECONDS:response.server.processing.time\",\n            \"STRING:request.status.last\",\n            \"HTTP.HEADER:response.header.etag\"})\n        public void setValue(final String name, final String value) {\n            results.put(name, value);\n        }\n\n        public Map<String, String> getResults() {\n            return results;\n        }\n    }\n\n    // ------------------------------------------\n\n    // LogFormat\n    // \"%h %a %A %l %u %t \\\"%r\\\" %>s %b %p \\\"%q\\\" \\\"%{Referer}i\\\" %D \\\"%{User-agent}i\\\" \\\"%{Cookie}i\\\" \\\"%{Set-Cookie}o\\\" \"\n    // +\"\\\"%{If-None-Match}i\\\" \\\"%{Etag}o\\\"\"\n    // fullcombined\n    private static final String LOG_FORMAT = \"%%%h %a %A %l %u %t \\\"%r\\\" %>s %b %p \\\"%q\\\" \\\"%!200,304,302{Referer}i\\\" %D \" +\n            \"\\\"%200{User-agent}i\\\" \\\"%{Cookie}i\\\" \\\"%{Set-Cookie}o\\\" \\\"%{If-None-Match}i\\\" \\\"%{Etag}o\\\"\";\n\n    // Because header names are case insensitive we use the lowercase version internally\n    // The modifiers ( like '!200,304,302') are to be removed.\n    // This next value is what should be used internally\n    private static final String EXPECTED_LOG_FORMAT = \"%%%h %a %A %l %u [%t] \\\"%r\\\" %>s %b %p \\\"%q\\\" \\\"%{referer}i\\\" %D \" +\n            \"\\\"%{user-agent}i\\\" \\\"%{cookie}i\\\" \\\"%{set-cookie}o\\\" \\\"%{if-none-match}i\\\" \\\"%{etag}o\\\"\";\n\n    // ------------------------------------------\n\n    /**\n     * Test of initialize method, of class ApacheHttpdLogParser.\n     */\n    @Test\n    void fullTest1() throws Exception {\n        String line = \"%127.0.0.1 127.0.0.1 127.0.0.1 - - [31/Dec/2012:23:49:40 +0100] \"\n                + \"\\\"GET /icons/powered_by_rh.png?aap=noot&res=1024x768 HTTP/1.1\\\" 200 1213 \"\n                + \"80 \\\"\\\" \\\"http://localhost/index.php?mies=wim\\\" 351 \"\n                + \"\\\"Mozilla/5.0 (X11; Linux i686 on x86_64; rv:11.0) Gecko/20100101 Firefox/11.0\\\" \"\n                + \"\\\"jquery-ui-theme=Eggplant\\\" \\\"Apache=127.0.0.1.1344635380111339; path=/; domain=.basjes.nl\\\" \\\"-\\\" \"\n                + \"\\\"\\\\\\\"3780ff-4bd-4c1ce3df91380\\\\\\\"\\\"\";\n\n        Parser<TestRecord> parser = new HttpdLoglineParser<>(TestRecord.class, LOG_FORMAT);\n\n        // Manually add an extra dissector\n        parser.addDissector(new ScreenResolutionDissector());\n        parser.addTypeRemapping(\"request.firstline.uri.query.res\", \"SCREENRESOLUTION\");\n        List<String> extraFields = new ArrayList<>();\n        extraFields.add(\"SCREENWIDTH:request.firstline.uri.query.res.width\");\n        extraFields.add(\"SCREENHEIGHT:request.firstline.uri.query.res.height\");\n        parser.addParseTarget(TestRecord.class.getMethod(\"setValue\", String.class, String.class), extraFields);\n\n        TestRecord record = new TestRecord();\n        parser.parse(record, line);\n        Map<String, String> results = record.getResults();\n\n        System.out.println(results.toString());\n\n        assertEquals(\"noot\", results.get(\"STRING:request.firstline.uri.query.aap\"));\n        assertEquals(null, results.get(\"STRING:request.firstline.uri.query.foo\"));\n        assertEquals(null, results.get(\"STRING:request.querystring.aap\"));\n        assertEquals(\"1024\", results.get(\"SCREENWIDTH:request.firstline.uri.query.res.width\"));\n        assertEquals(\"768\", results.get(\"SCREENHEIGHT:request.firstline.uri.query.res.height\"));\n\n        assertEquals(\"127.0.0.1\", results.get(\"IP:connection.client.ip\"));\n        assertEquals(null, results.get(\"NUMBER:connection.client.logname\"));\n        assertEquals(null, results.get(\"STRING:connection.client.user\"));\n        assertEquals(\"31/Dec/2012:23:49:40 +0100\", results.get(\"TIME.STAMP:request.receive.time\"));\n        assertEquals(\"1356994180000\", results.get(\"TIME.EPOCH:request.receive.time.epoch\"));\n        assertEquals(\"1\", results.get(\"TIME.WEEK:request.receive.time.weekofweekyear\"));\n        assertEquals(\"2013\", results.get(\"TIME.YEAR:request.receive.time.weekyear\"));\n        assertEquals(\"2012\", results.get(\"TIME.YEAR:request.receive.time.year\"));\n        assertEquals(\"40\", results.get(\"TIME.SECOND:request.receive.time.second\"));\n        assertEquals(\"/icons/powered_by_rh.png?aap=noot&res=1024x768\", results.get(\"HTTP.URI:request.firstline.uri\"));\n        assertEquals(\"200\", results.get(\"STRING:request.status.last\"));\n        assertEquals(\"1213\", results.get(\"BYTESCLF:response.body.bytes\"));\n        assertEquals(\"http://localhost/index.php?mies=wim\", results.get(\"HTTP.URI:request.referer\"));\n        assertEquals(\"wim\", results.get(\"STRING:request.referer.query.mies\"));\n        assertEquals(\"Mozilla/5.0 (X11; Linux i686 on x86_64; rv:11.0) Gecko/20100101 Firefox/11.0\",\n                results.get(\"HTTP.USERAGENT:request.user-agent\"));\n        assertEquals(\"31\", results.get(\"TIME.DAY:request.receive.time.day\"));\n        assertEquals(\"23\", results.get(\"TIME.HOUR:request.receive.time.hour\"));\n        assertEquals(\"December\", results.get(\"TIME.MONTHNAME:request.receive.time.monthname\"));\n        assertEquals(\"351\", results.get(\"MICROSECONDS:response.server.processing.time\"));\n        assertEquals(\"Apache=127.0.0.1.1344635380111339; path=/; domain=.basjes.nl\",\n                results.get(\"HTTP.SETCOOKIES:response.cookies\"));\n        assertEquals(\"jquery-ui-theme=Eggplant\", results.get(\"HTTP.COOKIES:request.cookies\"));\n        assertEquals(\"\\\"3780ff-4bd-4c1ce3df91380\\\"\", results.get(\"HTTP.HEADER:response.header.etag\"));\n\n        assertEquals(\"Eggplant\", results.get(\"HTTP.COOKIE:request.cookies.jquery-ui-theme\"));\n        assertEquals(\"Apache=127.0.0.1.1344635380111339; path=/; domain=.basjes.nl\", results.get(\"HTTP.SETCOOKIE:response.cookies.apache\"));\n        assertEquals(\".basjes.nl\", results.get(\"STRING:response.cookies.apache.domain\"));\n\n    }\n\n    // ------------------------------------------\n\n    @Test\n    void fullTest2() throws Exception {\n        Parser<TestRecord> parser = new HttpdLoglineParser<>(TestRecord.class, LOG_FORMAT);\n\n        String line = \"%127.0.0.1 127.0.0.1 127.0.0.1 - - [10/Aug/2012:23:55:11 +0200] \\\"GET /icons/powered_by_rh.png HTTP/1.1\\\" 200 1213 80\"\n                + \" \\\"\\\" \\\"http://localhost/\\\" 1306 \\\"Mozilla/5.0 (X11; Linux i686 on x86_64; rv:11.0) Gecko/20100101 Firefox/11.0\\\"\"\n                + \" \\\"jquery-ui-theme=Eggplant; Apache=127.0.0.1.1344635667182858\\\" \\\"-\\\" \\\"-\\\" \\\"\\\\\\\"3780ff-4bd-4c1ce3df91380\\\\\\\"\\\"\";\n\n        TestRecord record = new TestRecord();\n        parser.parse(record, line);\n        Map<String, String> results = record.getResults();\n\n        assertEquals(null, results.get(\"HTTP.QUERYSTRING:request.firstline.uri.query.foo\"));\n        assertEquals(\"127.0.0.1\", results.get(\"IP:connection.client.ip\"));\n        assertEquals(null, results.get(\"NUMBER:connection.client.logname\"));\n        assertEquals(null, results.get(\"STRING:connection.client.user\"));\n        assertEquals(\"10/Aug/2012:23:55:11 +0200\", results.get(\"TIME.STAMP:request.receive.time\"));\n        assertEquals(\"11\", results.get(\"TIME.SECOND:request.receive.time.second\"));\n        assertEquals(\"/icons/powered_by_rh.png\", results.get(\"HTTP.URI:request.firstline.uri\"));\n        assertEquals(\"200\", results.get(\"STRING:request.status.last\"));\n        assertEquals(\"1213\", results.get(\"BYTESCLF:response.body.bytes\"));\n        assertEquals(\"http://localhost/\", results.get(\"HTTP.URI:request.referer\"));\n        assertEquals(\"Mozilla/5.0 (X11; Linux i686 on x86_64; rv:11.0) Gecko/20100101 Firefox/11.0\",\n                results.get(\"HTTP.USERAGENT:request.user-agent\"));\n        assertEquals(\"10\", results.get(\"TIME.DAY:request.receive.time.day\"));\n        assertEquals(\"23\", results.get(\"TIME.HOUR:request.receive.time.hour\"));\n        assertEquals(\"August\", results.get(\"TIME.MONTHNAME:request.receive.time.monthname\"));\n        assertEquals(\"1306\", results.get(\"MICROSECONDS:response.server.processing.time\"));\n        assertEquals(null, results.get(\"HTTP.SETCOOKIES:response.cookies\"));\n        assertEquals(\"jquery-ui-theme=Eggplant; Apache=127.0.0.1.1344635667182858\",\n                results.get(\"HTTP.COOKIES:request.cookies\"));\n        assertEquals(\"\\\"3780ff-4bd-4c1ce3df91380\\\"\", results.get(\"HTTP.HEADER:response.header.etag\"));\n        // assertEquals(\"351\",results.get(\"COOKIE:request.cookie.jquery-ui-theme\"));\n    }\n\n    // ------------------------------------------\n\n    @Test\n    void fullTestTooLongUri() throws Exception {\n        Parser<TestRecord> parser = new HttpdLoglineParser<>(TestRecord.class, LOG_FORMAT);\n\n        String line = \"%127.0.0.1 127.0.0.1 127.0.0.1 - - [10/Aug/2012:23:55:11 +0200] \\\"GET /ImagineAURLHereThatIsTooLong\\\" 414 1213 80\"\n                + \" \\\"\\\" \\\"http://localhost/\\\" 1306 \\\"Mozilla/5.0 (X11; Linux i686 on x86_64; rv:11.0) Gecko/20100101 Firefox/11.0\\\"\"\n                + \" \\\"jquery-ui-theme=Eggplant; Apache=127.0.0.1.1344635667182858\\\" \\\"-\\\" \\\"-\\\" \\\"\\\\\\\"3780ff-4bd-4c1ce3df91380\\\\\\\"\\\"\";\n\n        TestRecord record = new TestRecord();\n        parser.parse(record, line);\n        Map<String, String> results = record.getResults();\n\n        // System.out.println(results.toString());\n\n        assertEquals(null, results.get(\"HTTP.QUERYSTRING:request.firstline.uri.query.foo\"));\n        assertEquals(\"127.0.0.1\", results.get(\"IP:connection.client.ip\"));\n        assertEquals(null, results.get(\"NUMBER:connection.client.logname\"));\n        assertEquals(null, results.get(\"STRING:connection.client.user\"));\n        assertEquals(\"10/Aug/2012:23:55:11 +0200\", results.get(\"TIME.STAMP:request.receive.time\"));\n        assertEquals(\"11\", results.get(\"TIME.SECOND:request.receive.time.second\"));\n        assertEquals(\"/ImagineAURLHereThatIsTooLong\", results.get(\"HTTP.URI:request.firstline.uri\"));\n        assertEquals(\"414\", results.get(\"STRING:request.status.last\"));\n        assertEquals(\"1213\", results.get(\"BYTESCLF:response.body.bytes\"));\n        assertEquals(\"http://localhost/\", results.get(\"HTTP.URI:request.referer\"));\n        assertEquals(\"Mozilla/5.0 (X11; Linux i686 on x86_64; rv:11.0) Gecko/20100101 Firefox/11.0\",\n                results.get(\"HTTP.USERAGENT:request.user-agent\"));\n        assertEquals(\"10\", results.get(\"TIME.DAY:request.receive.time.day\"));\n        assertEquals(\"23\", results.get(\"TIME.HOUR:request.receive.time.hour\"));\n        assertEquals(\"August\", results.get(\"TIME.MONTHNAME:request.receive.time.monthname\"));\n        assertEquals(\"1306\", results.get(\"MICROSECONDS:response.server.processing.time\"));\n        assertEquals(null, results.get(\"HTTP.SETCOOKIES:response.cookies\"));\n        assertEquals(\"jquery-ui-theme=Eggplant; Apache=127.0.0.1.1344635667182858\",\n                results.get(\"HTTP.COOKIES:request.cookies\"));\n        assertEquals(\"\\\"3780ff-4bd-4c1ce3df91380\\\"\", results.get(\"HTTP.HEADER:response.header.etag\"));\n        // assertEquals(\"351\",results.get(\"COOKIE:request.cookie.jquery-ui-theme\"));\n    }\n\n    // ------------------------------------------\n\n    public static class TestRecordMissing {\n        @SuppressWarnings({\"UnusedDeclaration\", \"EmptyMethod\"})\n        @Field({ \"STRING:request.firstline.uri.query.ThisShouldNOTBeMissing\", \"HEADER:response.header.Etag.ThisShouldBeMissing\" })\n        public void dummy(final String name, final String value) {\n        }\n    }\n\n    @Test\n    void testMissing() throws Exception {\n        try {\n            Parser<TestRecordMissing> parser = new HttpdLoglineParser<>(TestRecordMissing.class, LOG_FORMAT);\n            parser.parse(\"\"); // Just to trigger the internal assembly of things (that should fail).\n            fail(\"Missing exception.\");\n        } catch (MissingDissectorsException e) {\n            assertTrue(e.getMessage().contains(\"HEADER:response.header.etag.thisshouldbemissing\"));\n        }\n    }\n\n    // ------------------------------------------\n\n    public static class TestRecordMissing2 {\n        @SuppressWarnings({\"UnusedDeclaration\", \"EmptyMethod\"})\n        @Field({ \"BLURP:request.firstline.uri.query.ThisShouldBeMissing\", \"HTTP.HEADER:response.header.etag\" })\n        public void dummy(final String name, final String value) {\n        }\n    }\n\n    @Test\n    void testMissing2() throws Exception {\n        try {\n            Parser<TestRecordMissing2> parser = new HttpdLoglineParser<>(TestRecordMissing2.class, LOG_FORMAT);\n            parser.parse(\"\"); // Just to trigger the internal assembly of things (that should fail).\n            fail(\"Missing exception.\");\n        } catch (MissingDissectorsException e) {\n            assertTrue(e.getMessage().contains(\"BLURP:request.firstline.uri.query.thisshouldbemissing\"));\n        }\n    }\n\n    // ------------------------------------------\n\n    @Test\n    void testGetPossiblePaths() {\n        Parser<TestRecord> parser = new HttpdLoglineParser<>(TestRecord.class, LOG_FORMAT);\n\n        List<String> paths = parser.getPossiblePaths(5);\n        assertEquals(true, paths.contains(\"TIME.SECOND:request.receive.time.second\"));\n        assertEquals(true, paths.contains(\"STRING:request.firstline.uri.query.*\"));\n        assertEquals(true, paths.contains(\"STRING:response.cookies.*.expires\"));\n        assertEquals(true, paths.contains(\"HTTP.HEADER:response.header.etag\"));\n\n        assertEquals(false, paths.contains(\"FIXED_STRING:fixed_string\"));\n    }\n\n    // ------------------------------------------\n\n    @Test\n    void testGetPossiblePathsWithUnusableLogFormat() {\n        Parser<TestRecord> parser = new HttpdLoglineParser<>(TestRecord.class, \"Empty\");\n\n        List<String> paths = parser.getPossiblePaths(5);\n        assertTrue(paths == null || paths.isEmpty(), \"The output should be empty!\");\n    }\n\n    // ------------------------------------------\n    @Test\n    void testLogFormatCleanup(){\n        ApacheHttpdLogFormatDissector d = new ApacheHttpdLogFormatDissector();\n\n        assertEquals(\"foo\", d.cleanupLogFormat(\"foo\"));\n        assertEquals(EXPECTED_LOG_FORMAT, d.cleanupLogFormat(LOG_FORMAT));\n        assertEquals(\"%{user-agent}i %% %{referer}i %s %{user-agent}i %% %{referer}i\",\n                d.cleanupLogFormat(\"%400,501{User-agent}i %% %!200,304,302{Referer}i %s %{User-agent}i %% %{Referer}i\"));\n    }\n\n    @Test\n    void verifyCommonFormatNamesMapping() {\n        ApacheHttpdLogFormatDissector dissector = new ApacheHttpdLogFormatDissector(\"combined\");\n        assertEquals(\"%h %l %u %t \\\"%r\\\" %>s %b \\\"%{Referer}i\\\" \\\"%{User-Agent}i\\\"\", dissector.getLogFormat());\n    }\n\n    // ------------------------------------------\n\n    public static class EmptyTestRecord extends HashMap<String, String> {\n        @Override\n        public String put(String key, String value) {\n            return super.put(key, value);\n        }\n        private static final long serialVersionUID = 1L;\n    }\n\n    @Test\n    void testQueryStringDissector() throws Exception {\n        String logformat = \"%r\";\n\n        Parser<EmptyTestRecord> parser = new HttpdLoglineParser<>(EmptyTestRecord.class, logformat);\n\n        String[] params = {\"STRING:request.firstline.uri.query.foo\",\n                           \"STRING:request.firstline.uri.query.bar\",\n                           \"HTTP.PATH:request.firstline.uri.path\",\n                           \"HTTP.QUERYSTRING:request.firstline.uri.query\",\n                           \"HTTP.REF:request.firstline.uri.ref\"\n        };\n        parser.addParseTarget(EmptyTestRecord.class.getMethod(\"put\", String.class, String.class), Arrays.asList(params));\n\n        EmptyTestRecord record = new EmptyTestRecord();\n\n        parser.parse(record, \"GET /index.html HTTP/1.1\");\n        assertEquals(null, record.get(\"STRING:request.firstline.uri.query.foo\"));\n        assertEquals(null, record.get(\"STRING:request.firstline.uri.query.bar\"));\n        assertEquals(\"/index.html\", record.get(\"HTTP.PATH:request.firstline.uri.path\"));\n        assertEquals(null, record.get(\"HTTP.QUERYSTRING:request.firstline.uri.query\"));\n        assertEquals(null, record.get(\"HTTP.REF:request.firstline.uri.ref\"));\n\n        record.clear();\n        parser.parse(record, \"GET /index.html?foo HTTP/1.1\");\n        assertEquals(\"\", record.get(\"STRING:request.firstline.uri.query.foo\"));\n        assertEquals(null, record.get(\"STRING:request.firstline.uri.query.bar\"));\n        assertEquals(\"/index.html\", record.get(\"HTTP.PATH:request.firstline.uri.path\"));\n        assertEquals(\"&foo\", record.get(\"HTTP.QUERYSTRING:request.firstline.uri.query\"));\n        assertEquals(null, record.get(\"HTTP.REF:request.firstline.uri.ref\"));\n\n        record.clear();\n        parser.parse(record, \"GET /index.html&foo HTTP/1.1\");\n        assertEquals(\"\", record.get(\"STRING:request.firstline.uri.query.foo\"));\n        assertEquals(null, record.get(\"STRING:request.firstline.uri.query.bar\"));\n        assertEquals(\"/index.html\", record.get(\"HTTP.PATH:request.firstline.uri.path\"));\n        assertEquals(\"&foo\", record.get(\"HTTP.QUERYSTRING:request.firstline.uri.query\"));\n        assertEquals(null, record.get(\"HTTP.REF:request.firstline.uri.ref\"));\n\n        record.clear();\n        parser.parse(record, \"GET /index.html?foo=foofoo# HTTP/1.1\");\n        assertEquals(\"foofoo\", record.get(\"STRING:request.firstline.uri.query.foo\"));\n        assertEquals(null, record.get(\"STRING:request.firstline.uri.query.bar\"));\n        assertEquals(\"/index.html\", record.get(\"HTTP.PATH:request.firstline.uri.path\"));\n        assertEquals(\"&foo=foofoo\", record.get(\"HTTP.QUERYSTRING:request.firstline.uri.query\"));\n        assertEquals(null, record.get(\"HTTP.REF:request.firstline.uri.ref\"));\n\n        record.clear();\n        parser.parse(record, \"GET /index.html&foo=foofoo HTTP/1.1\");\n        assertEquals(\"foofoo\", record.get(\"STRING:request.firstline.uri.query.foo\"));\n        assertEquals(null, record.get(\"STRING:request.firstline.uri.query.bar\"));\n        assertEquals(\"/index.html\", record.get(\"HTTP.PATH:request.firstline.uri.path\"));\n        assertEquals(\"&foo=foofoo\", record.get(\"HTTP.QUERYSTRING:request.firstline.uri.query\"));\n        assertEquals(null, record.get(\"HTTP.REF:request.firstline.uri.ref\"));\n\n        record.clear();\n        parser.parse(record, \"GET /index.html?bar&foo=foofoo# HTTP/1.1\");\n        assertEquals(\"foofoo\", record.get(\"STRING:request.firstline.uri.query.foo\"));\n        assertEquals(\"\", record.get(\"STRING:request.firstline.uri.query.bar\"));\n        assertEquals(\"/index.html\", record.get(\"HTTP.PATH:request.firstline.uri.path\"));\n        assertEquals(\"&bar&foo=foofoo\", record.get(\"HTTP.QUERYSTRING:request.firstline.uri.query\"));\n        assertEquals(null, record.get(\"HTTP.REF:request.firstline.uri.ref\"));\n\n        record.clear();\n        parser.parse(record, \"GET /index.html?bar&foo=foofoo#bookmark HTTP/1.1\");\n        assertEquals(\"foofoo\", record.get(\"STRING:request.firstline.uri.query.foo\"));\n        assertEquals(\"\", record.get(\"STRING:request.firstline.uri.query.bar\"));\n        assertEquals(\"/index.html\", record.get(\"HTTP.PATH:request.firstline.uri.path\"));\n        assertEquals(\"&bar&foo=foofoo\", record.get(\"HTTP.QUERYSTRING:request.firstline.uri.query\"));\n        assertEquals(\"bookmark\", record.get(\"HTTP.REF:request.firstline.uri.ref\"));\n\n        record.clear();\n        parser.parse(record, \"GET /index.html?bar=barbar&foo=foofoo#bookmark HTTP/1.1\");\n        assertEquals(\"foofoo\", record.get(\"STRING:request.firstline.uri.query.foo\"));\n        assertEquals(\"barbar\", record.get(\"STRING:request.firstline.uri.query.bar\"));\n        assertEquals(\"/index.html\", record.get(\"HTTP.PATH:request.firstline.uri.path\"));\n        assertEquals(\"&bar=barbar&foo=foofoo\", record.get(\"HTTP.QUERYSTRING:request.firstline.uri.query\"));\n        assertEquals(\"bookmark\", record.get(\"HTTP.REF:request.firstline.uri.ref\"));\n\n        record.clear();\n        parser.parse(record, \"GET /index.html&bar=barbar&foo=foofoo#bla HTTP/1.1\");\n        assertEquals(\"foofoo\", record.get(\"STRING:request.firstline.uri.query.foo\"));\n        assertEquals(\"barbar\", record.get(\"STRING:request.firstline.uri.query.bar\"));\n        assertEquals(\"/index.html\", record.get(\"HTTP.PATH:request.firstline.uri.path\"));\n        assertEquals(\"&bar=barbar&foo=foofoo\", record.get(\"HTTP.QUERYSTRING:request.firstline.uri.query\"));\n        assertEquals(\"bla\", record.get(\"HTTP.REF:request.firstline.uri.ref\"));\n\n        record.clear();\n        parser.parse(record, \"GET /index.html&bar=barbar?foo=foofoo HTTP/1.1\");\n        assertEquals(\"foofoo\", record.get(\"STRING:request.firstline.uri.query.foo\"));\n        assertEquals(\"barbar\", record.get(\"STRING:request.firstline.uri.query.bar\"));\n        assertEquals(\"/index.html\", record.get(\"HTTP.PATH:request.firstline.uri.path\"));\n        assertEquals(\"&bar=barbar&foo=foofoo\", record.get(\"HTTP.QUERYSTRING:request.firstline.uri.query\"));\n        assertEquals(null, record.get(\"HTTP.REF:request.firstline.uri.ref\"));\n\n    }\n\n    // ------------------------------------------\n\n    /**\n     * Test of mod_reqtimeout 408 status code\n     * Assume  mod_reqtimeout is enabled and absolutely no data is entered by a client\n     * after making the connection. The result is a http 408 status code and a logline that has proven to\n     * result in several fields failing to be parsed because they are different than the specifications.\n     */\n    @Test\n    void test408ModReqTimeout() throws Exception {\n\n        final String logformat =\n            \"\\\"%%\\\" \\\"%a\\\" \\\"%{c}a\\\" \\\"%A\\\" \\\"%B\\\" \\\"%b\\\" \\\"%D\\\" \\\"%f\\\" \\\"%h\\\" \\\"%H\\\" \\\"%k\\\" \" +\n            \"\\\"%l\\\" \\\"%L\\\" \\\"%m\\\" \\\"%p\\\" \\\"%{canonical}p\\\" \\\"%{local}p\\\" \\\"%{remote}p\\\" \\\"%P\\\" \\\"%{pid}P\\\" \\\"%{tid}P\\\"\" +\n            \" \\\"%{hextid}P\\\" \\\"%q\\\" \\\"%r\\\" \\\"%R\\\" \\\"%s\\\" \\\"%>s\\\" \\\"%t\\\" \\\"%{msec}t\\\" \\\"%{begin:msec}t\\\" \\\"%{end:msec}t\" +\n            \"\\\" \\\"%{usec}t\\\" \\\"%{begin:usec}t\\\" \\\"%{end:usec}t\\\" \\\"%{msec_frac}t\\\" \\\"%{begin:msec_frac}t\\\" \\\"%{end:mse\" +\n            \"c_frac}t\\\" \\\"%{usec_frac}t\\\" \\\"%{begin:usec_frac}t\\\" \\\"%{end:usec_frac}t\\\" \\\"%T\\\" \\\"%u\\\" \\\"%U\\\" \\\"%v\\\" \\\"\" +\n            \"%V\\\" \\\"%X\\\" \\\"%I\\\" \\\"%O\\\" \\\"%{cookie}i\\\" \\\"%{set-cookie}o\\\" \\\"%{user-agent}i\\\" \\\"%{referer}i\\\"\";\n\n        String line200 = \"\\\"%\\\" \\\"127.0.0.1\\\" \\\"127.0.0.1\\\" \\\"127.0.0.1\\\" \\\"3186\\\" \\\"3186\\\" \\\"1302\\\" \\\"/var/www/html/index.html\\\" \" +\n            \"\\\"127.0.0.1\\\" \\\"HTTP/1.1\\\" \\\"0\\\" \\\"-\\\" \\\"-\\\" \\\"GET\\\" \\\"80\\\" \\\"80\\\" \\\"80\\\" \\\"50142\\\" \\\"10344\\\" \\\"10344\\\" \" +\n            \"\\\"139854162249472\\\" \\\"139854162249472\\\" \\\"\\\" \\\"GET / HTTP/1.1\\\" \\\"-\\\" \\\"200\\\" \\\"200\\\" \" +\n            \"\\\"[09/Aug/2016:22:57:59 +0200]\\\" \\\"1470776279833\\\" \\\"1470776279833\\\" \\\"1470776279835\\\" \\\"1470776279833934\\\" \" +\n            \"\\\"1470776279833934\\\" \\\"1470776279835236\\\" \\\"833\\\" \\\"833\\\" \\\"835\\\" \\\"833934\\\" \\\"833934\\\" \\\"835236\\\" \\\"0\\\" \" +\n            \"\\\"-\\\" \\\"/index.html\\\" \\\"committer.lan.basjes.nl\\\" \\\"localhost\\\" \\\"+\\\" \\\"490\\\" \\\"3525\\\" \\\"-\\\" \\\"-\\\" \" +\n            \"\\\"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36\\\" \\\"-\\\"\";\n\n        String line408 = \"\\\"%\\\" \\\"127.0.0.1\\\" \\\"127.0.0.1\\\" \\\"127.0.0.1\\\" \\\"0\\\" \\\"-\\\" \\\"34\\\" \\\"-\\\" \" +\n            \"\\\"127.0.0.1\\\" \\\"HTTP/1.0\\\" \\\"0\\\" \\\"-\\\" \\\"-\\\" \\\"-\\\" \\\"80\\\" \\\"80\\\" \\\"80\\\" \\\"50150\\\" \\\"10344\\\" \\\"10344\\\" \" +\n            \"\\\"139854067267328\\\" \\\"139854067267328\\\" \\\"\\\" \\\"-\\\" \\\"-\\\" \\\"408\\\" \\\"408\\\" \" +\n            \"\\\"[09/Aug/2016:22:59:14 +0200]\\\" \\\"1470776354625\\\" \\\"1470776354625\\\" \\\"1470776354625\\\" \\\"1470776354625377\\\" \" +\n            \"\\\"1470776354625377\\\" \\\"1470776354625411\\\" \\\"625\\\" \\\"625\\\" \\\"625\\\" \\\"625377\\\" \\\"625377\\\" \\\"625411\\\" \\\"0\\\" \" +\n            \"\\\"-\\\" \\\"-\\\" \\\"committer.lan.basjes.nl\\\" \\\"committer.lan.basjes.nl\\\" \\\"-\\\" \\\"0\\\" \\\"0\\\" \\\"-\\\" \\\"-\\\" \\\"-\\\" \\\"-\\\"\";\n\n        Parser<EmptyTestRecord> parser =\n            new HttpdLoglineParser<>(EmptyTestRecord.class, logformat)\n            .addParseTarget(EmptyTestRecord.class.getMethod(\"put\", String.class, String.class),\n                            \"STRING:request.firstline.uri.query.foo\");\n        parser.parse(new EmptyTestRecord(), line200);\n        parser.parse(new EmptyTestRecord(), line408);\n    }\n\n    @Test\n    void testFailOnMissingDissectors() {\n        assertThrows(MissingDissectorsException.class, () -> {\n            String line = \"[09/Aug/2016:22:57:59 +0200]\";\n\n            String[] params = {\n                \"STRING:request.firstline.uri.query.foo\",\n                \"TIME.EPOCH:request.receive.time.epoch\",\n            };\n\n            new HttpdLoglineParser<>(EmptyTestRecord.class, \"%t\")\n                .addParseTarget(EmptyTestRecord.class.getMethod(\"put\", String.class, String.class), Arrays.asList(params))\n                .failOnMissingDissectors()\n                .parse(new EmptyTestRecord(), line);\n        });\n    }\n\n    @Test\n    void testIgnoreMissingDissectors() throws Exception {\n        String line = \"[09/Aug/2016:22:57:59 +0200]\";\n\n        new HttpdLoglineParser<>(EmptyTestRecord.class, \"%t\")\n            .addParseTarget(EmptyTestRecord.class.getMethod(\"put\", String.class, String.class),\n                            Arrays.asList(\"STRING:request.firstline.uri.query.foo\",\n                                          \"TIME.EPOCH:request.receive.time.epoch\"))\n            .ignoreMissingDissectors()\n            .parse(new EmptyTestRecord(), line);\n    }\n\n    @Test\n    void testExternalExample() {\n        // Found on 2022-06-10 on\n        // https://github.com/cdapio/cdap/blob/develop/cdap-docs/user-guide/source/data-preparation/directives/parse-as-log.rst\n        String logFormat = \"%t %u [%D %h %{True-Client-IP}i %{UNIQUE_ID}e %r] %{Cookie}i %s \\\"%{User-Agent}i\\\" \\\"%{host}i\\\" %l %b %{Referer}i\";\n        String logLine = \"[03/Dec/2013:10:53:59 +0000] - [32002 10.102.4.254 195.229.241.182 Up24RwpmBAwAAA1LWJsAAAAR GET \" +\n            \"/content/dam/Central_Library/Street_Shots/Youth/2012/09sep/LFW/Gallery_03/LFW_SS13_SEPT_12_777.jpg.\" +\n            \"image.W0N539E3452S3991w313.original.jpg HTTP/1.1] __utmc=94539802; dtCookie=EFD9D09B6A2E1789F1329FC1\" +\n            \"381A356A|_default|1; dtPC=471217988_141#_load_; Carte::KerberosLexicon_getdomain=6701c1320dd96688b2e\" +\n            \"40b92ce748eee7ae99722; UserData=Username%3ALSHARMA%3AHomepage%3A1%3AReReg%3A0%3ATrialist%3A0%3ALangua\" +\n            \"ge%3Aen%3ACcode%3Aae%3AForceReReg%3A0; UserID=1375493%3A12345%3A1234567890%3A123%3Accode%3Aae; USER_D\" +\n            \"ATA=1375493%3ALSharma%3ALokesh%3ASharma%3Alokesh.sharma%40landmarkgroup.com%3A0%3A1%3Aen%3Aae%3A%3Ado\" +\n            \"main%3A1386060868.51392%3A6701c1320dd96688b2e40b92ce748eee7ae99722; MODE=FONTIS; __utma=94539802.9110\" +\n            \"97326.1339390457.1386060848.1386065609.190; __utmz=94539802.1384758205.177.38.utmcsr=google|utmccn=(o\" +\n            \"rganic)|utmcmd=organic|utmctr=(not%20provided); __kti=1339390460526,http%3A%2F%2Fwww.domain.com%2F,;\" +\n            \"__ktv=28e8-6c4-be3-ce54137d9e48271; WT_FPC=id=2.50.27.157-3067016480.30226245:lv=1386047044279:ss=138\" +\n            \"6046439530; _opt_vi_3FNG8DZU=42880957-D2F1-4DC5-AF16-FEF88891D24E; __hstc=145721067.750d315a49c642681\" +\n            \"92826b3911a4e5a.1351772962050.1381151113005.1381297633204.66; hsfirstvisit=http%3A%2F%2Fwww.domain.co\" +\n            \"m%2F|http%3A%2F%2Fwww.google.co.in%2Furl%3Fsa%3Dt%26rct%3Dj%26q%3Ddomain.com%26source%3Dweb%26cd%3D1%\" +\n            \"26ved%3D0CB0QFjAA%26url%3Dhttp%3A%2F%2Fwww.domain.com%2F%26ei%3DDmuSULW3AcTLhAfJ24CoDA%26usg%3DAFQjCN\" +\n            \"GvPmmyn8Bk67OUv-HwjVU4Ff3q1w|1351772962000; hubspotutk=750d315a49c64268192826b3911a4e5a; __ptca=14572\" +\n            \"1067.jQ7lN5U3C4eN.1351758562.1381136713.1381283233.66; __ptv_62vY4e=jQ7lN5U3C4eN; __pti_62vY4e=jQ7lN5\" +\n            \"U3C4eN; __ptcz=145721067.1351758562.1.0.ptmcsr=google|ptmcmd=organic|ptmccn=(organic)|ptmctr=domain.\" +\n            \"com; RM=Lsharma%3Ac163b6097f90d2869e537f95900e1c464daa8fb9; wcid=Up2cRApmBAwAAFOiVhcAAAAH%3Af32e5e5f5\" +\n            \"b593175bfc71af082ab26e4055efeb6; __utmb=94539802.71.9.1386067462709; edge_auth=ip%3D195.229.241.182~\" +\n            \"expires%3D1386069280~access%3D%2Fapps%2F%2A%21%2Fbin%2F%2A%21%2Fcontent%2F%2A%21%2Fetc%2F%2A%21%2Fho\" +\n            \"me%2F%2A%21%2Flibs%2F%2A%21%2Freport%2F%2A%21%2Fsection%2F%2A%21%2Fdomain%2F%2A~md5%3D5b47f341723924\" +\n            \"87dcd44c1d837e2e54; has_js=1; SECTION=%2Fcontent%2Fsection%2Finspiration-design%2Fstreet-shots.html;\" +\n            \"JSESSIONID=b9377099-7708-45ae-b6e7-c575ffe82187; WT_FPC=id=2.50.27.157-3067016480.30226245:lv=138605\" +\n            \"3618209:ss=1386053618209; USER_GROUP=LSharma%3Afalse; NSC_wtfswfs_xfcgbsn40-41=ffffffff096e1a1d45525\" +\n            \"d5f4f58455e445a4a423660 200 \\\"Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)\\\" \" +\n            \"\\\"www.domain.com\\\" - 24516 http://www.domain.com/content/report/Street_Shots/Youth/Global_round_up/201\" +\n            \"3/01_Jan/mens_youth_stylingglobalround-up1.html\";\n\n        DissectorTester.create()\n            .verbose()\n            .withParser(new HttpdLoglineParser<>(nl.basjes.parse.core.test.TestRecord.class, logFormat)\n                .addTypeRemapping(\"server.environment.unique_id\", \"MOD_UNIQUE_ID\"))\n            .withInput(logLine)\n            .printPossible()\n            .printAllPossibleValues()\n            // A normal field\n            .expect(\"IP:connection.client.host\", \"10.102.4.254\")\n            // The timestamp was parsed and normalized to the Epoch milliseconds\n            .expect(\"TIME.EPOCH:request.receive.time.epoch\", \"1386068039000\")\n            // A cookie with a very strange name\n            .expect(\"HTTP.COOKIE:request.cookies.carte::kerberoslexicon_getdomain\", \"6701c1320dd96688b2e40b92ce748eee7ae99722\")\n            // A cookie value that needed a lot of decoding\n            .expect(\"HTTP.COOKIE:request.cookies.hsfirstvisit\", \"http://www.domain.com/|http://www.google.co.in/url?sa=t&rct=j&q=domain.com&source=web&cd=1&ved=0CB0QFjAA&url=http://www.domain.com/&ei=DmuSULW3AcTLhAfJ24CoDA&usg=AFQjCNGvPmmyn8Bk67OUv-HwjVU4Ff3q1w|1351772962000\")\n            // This is the IP which was extracted from the UNIQUE_ID ( \"Up24RwpmBAwAAA1LWJsAAAAR\" in this case).\n            .expect(\"IP:server.environment.unique_id.ip\", \"10.102.4.12\")\n            .checkExpectations();\n    }\n\n\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/test/java/nl/basjes/parse/httpdlog/BasicOverallTest.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage nl.basjes.parse.httpdlog;\n\nimport nl.basjes.parse.core.Parser;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\nclass BasicOverallTest {\n    public static class MyRecord {\n\n        private final List<String> results = new ArrayList<>();\n\n        @SuppressWarnings({\"unused\"}) // Used via reflection\n        public void setValue(final String name, final String value) {\n            results.add(name + '=' + value);\n        }\n\n        public String toString() {\n            StringBuilder sb = new StringBuilder();\n            sb.append(\" ----------- BEGIN ----------\\n\");\n            for (String value : results) {\n                sb.append(value).append('\\n');\n            }\n            sb.append(\" ------------ END -----------\\n\");\n            sb.append(\"\\n\");\n            return sb.toString();\n        }\n\n        public void clear() {\n            results.clear();\n        }\n    }\n\n    private static final String LOG_FORMAT =\n            \"\\\"%%\\\" \\\"%a\\\" \\\"%{c}a\\\" \\\"%A\\\" \\\"%B\\\" \\\"%b\\\" \\\"%D\\\" \\\"%f\\\" \\\"%h\\\" \\\"%H\\\" \\\"%k\\\" \" +\n            \"\\\"%l\\\" \\\"%L\\\" \\\"%m\\\" \\\"%p\\\" \\\"%{canonical}p\\\" \\\"%{local}p\\\" \\\"%{remote}p\\\" \\\"%P\\\" \\\"%{pid}P\\\" \\\"%{tid}P\\\"\" +\n            \" \\\"%{hextid}P\\\" \\\"%q\\\" \\\"%r\\\" \\\"%R\\\" \\\"%s\\\" \\\"%>s\\\" \\\"%t\\\" \\\"%{msec}t\\\" \\\"%{begin:msec}t\\\" \\\"%{end:msec}t\" +\n            \"\\\" \\\"%{usec}t\\\" \\\"%{begin:usec}t\\\" \\\"%{end:usec}t\\\" \\\"%{msec_frac}t\\\" \\\"%{begin:msec_frac}t\\\" \\\"%{end:mse\" +\n            \"c_frac}t\\\" \\\"%{usec_frac}t\\\" \\\"%{begin:usec_frac}t\\\" \\\"%{end:usec_frac}t\\\" \\\"%T\\\" \\\"%u\\\" \\\"%U\\\" \\\"%v\\\" \\\"\" +\n            \"%V\\\" \\\"%X\\\" \\\"%I\\\" \\\"%O\\\" \\\"%{cookie}i\\\" \\\"%{set-cookie}o\\\" \\\"%{user-agent}i\\\" \\\"%{referer}i\\\"\";\n\n    private static final String[] LOG_LINES = {\n        \"\\\"%\\\" \\\"172.17.42.1\\\" \\\"172.17.42.1\\\" \\\"172.17.0.2\\\" \\\"4880\\\" \\\"4880\\\" \\\"652\\\" \\\"/usr/share/httpd/noindex/ind\" +\n                \"ex.html\\\" \\\"172.17.42.1\\\" \\\"HTTP/1.1\\\" \\\"0\\\" \\\"-\\\" \\\"VG9exZ0MX@uqta4OldejvQAAAAA\\\" \\\"GET\\\" \\\"80\\\" \\\"8\" +\n                \"0\\\" \\\"80\\\" \\\"43417\\\" \\\"126\\\" \\\"126\\\" \\\"140597540726848\\\" \\\"140597540726848\\\" \\\"\\\" \\\"GET / HTTP/1.1\\\" \" +\n                \"\\\"httpd/unix-directory\\\" \\\"403\\\" \\\"403\\\" \\\"[21/Nov/2014:15:48:21 +0000]\\\" \\\"1416584901018\\\" \\\"1416584\" +\n                \"901018\\\" \\\"1416584901018\\\" \\\"1416584901018010\\\" \\\"1416584901018010\\\" \\\"1416584901018670\\\" \\\"018\\\" \\\"0\" +\n                \"18\\\" \\\"018\\\" \\\"018010\\\" \\\"018010\\\" \\\"018670\\\" \\\"0\\\" \\\"-\\\" \\\"/\\\" \\\"172.17.0.2\\\" \\\"172.17.0.2\\\" \\\"+\\\" \" +\n                \"\\\"367\\\" \\\"5188\\\" \\\"-\\\" \\\"-\\\" \\\"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko)\" +\n                \" Chrome/38.0.2125.122 Safari/537.36\\\" \\\"-\\\"\",\n        \"\\\"%\\\" \\\"172.17.42.1\\\" \\\"172.17.42.1\\\" \\\"172.17.0.2\\\" \\\"0\\\" \\\"-\\\" \\\"302\\\" \\\"/usr/share/httpd/noindex/css/boots\" +\n                \"trap.min.css\\\" \\\"172.17.42.1\\\" \\\"HTTP/1.1\\\" \\\"1\\\" \\\"-\\\" \\\"-\\\" \\\"GET\\\" \\\"80\\\" \\\"80\\\" \\\"80\\\" \\\"43417\\\" \" +\n                \"\\\"126\\\" \\\"126\\\" \\\"140597540726848\\\" \\\"140597540726848\\\" \\\"\\\" \\\"GET /css/bootstrap.min.css HTTP/1.1\\\" \" +\n                \"\\\"-\\\" \\\"304\\\" \\\"304\\\" \\\"[21/Nov/2014:15:48:21 +0000]\\\" \\\"1416584901087\\\" \\\"1416584901087\\\" \\\"14165849\" +\n                \"01087\\\" \\\"1416584901087115\\\" \\\"1416584901087115\\\" \\\"1416584901087417\\\" \\\"087\\\" \\\"087\\\" \\\"087\\\" \\\"0871\" +\n                \"15\\\" \\\"087115\\\" \\\"087417\\\" \\\"0\\\" \\\"-\\\" \\\"/css/bootstrap.min.css\\\" \\\"172.17.0.2\\\" \\\"172.17.0.2\\\" \\\"+\\\"\" +\n                \" \\\"448\\\" \\\"180\\\" \\\"-\\\" \\\"-\\\" \\\"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko)\" +\n                \" Chrome/38.0.2125.122 Safari/537.36\\\" \\\"http://172.17.0.2/\\\"\",\n        \"\\\"%\\\" \\\"172.17.42.1\\\" \\\"172.17.42.1\\\" \\\"172.17.0.2\\\" \\\"0\\\" \\\"-\\\" \\\"373\\\" \\\"/usr/share/httpd/noindex/css/open-\" +\n                \"sans.css\\\" \\\"172.17.42.1\\\" \\\"HTTP/1.1\\\" \\\"0\\\" \\\"-\\\" \\\"-\\\" \\\"GET\\\" \\\"80\\\" \\\"80\\\" \\\"80\\\" \\\"43418\\\" \\\"12\" +\n                \"7\\\" \\\"127\\\" \\\"140597540726848\\\" \\\"140597540726848\\\" \\\"\\\" \\\"GET /css/open-sans.css HTTP/1.1\\\" \\\"-\\\" \\\"\" +\n                \"304\\\" \\\"304\\\" \\\"[21/Nov/2014:15:48:21 +0000]\\\" \\\"1416584901087\\\" \\\"1416584901087\\\" \\\"1416584901087\\\" \" +\n                \"\\\"1416584901087430\\\" \\\"1416584901087430\\\" \\\"1416584901087803\\\" \\\"087\\\" \\\"087\\\" \\\"087\\\" \\\"087430\\\" \\\"0\" +\n                \"87430\\\" \\\"087803\\\" \\\"0\\\" \\\"-\\\" \\\"/css/open-sans.css\\\" \\\"172.17.0.2\\\" \\\"172.17.0.2\\\" \\\"+\\\" \\\"444\\\" \\\"1\" +\n                \"81\\\" \\\"-\\\" \\\"-\\\" \\\"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0\" +\n                \".2125.122 Safari/537.36\\\" \\\"http://172.17.0.2/\\\"\",\n        \"\\\"%\\\" \\\"172.17.42.1\\\" \\\"172.17.42.1\\\" \\\"172.17.0.2\\\" \\\"0\\\" \\\"-\\\" \\\"381\\\" \\\"/usr/share/httpd/noindex/images/ap\" +\n                \"ache_pb.gif\\\" \\\"172.17.42.1\\\" \\\"HTTP/1.1\\\" \\\"0\\\" \\\"-\\\" \\\"-\\\" \\\"GET\\\" \\\"80\\\" \\\"80\\\" \\\"80\\\" \\\"43419\\\" \" +\n                \"\\\"128\\\" \\\"128\\\" \\\"140597540726848\\\" \\\"140597540726848\\\" \\\"\\\" \\\"GET /images/apache_pb.gif HTTP/1.1\\\" \" +\n                \"\\\"-\\\" \\\"304\\\" \\\"304\\\" \\\"[21/Nov/2014:15:48:21 +0000]\\\" \\\"1416584901087\\\" \\\"1416584901087\\\" \\\"14165849\" +\n                \"01087\\\" \\\"1416584901087445\\\" \\\"1416584901087445\\\" \\\"1416584901087826\\\" \\\"087\\\" \\\"087\\\" \\\"087\\\" \\\"0874\" +\n                \"45\\\" \\\"087445\\\" \\\"087826\\\" \\\"0\\\" \\\"-\\\" \\\"/images/apache_pb.gif\\\" \\\"172.17.0.2\\\" \\\"172.17.0.2\\\" \\\"+\\\" \" +\n                \"\\\"448\\\" \\\"180\\\" \\\"-\\\" \\\"-\\\" \\\"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) \" +\n                \"Chrome/38.0.2125.122 Safari/537.36\\\" \\\"http://172.17.0.2/\\\"\",\n        \"\\\"%\\\" \\\"172.17.42.1\\\" \\\"172.17.42.1\\\" \\\"172.17.0.2\\\" \\\"0\\\" \\\"-\\\" \\\"269\\\" \\\"/usr/share/httpd/noindex/images/po\" +\n                \"weredby.png\\\" \\\"172.17.42.1\\\" \\\"HTTP/1.1\\\" \\\"1\\\" \\\"-\\\" \\\"-\\\" \\\"GET\\\" \\\"80\\\" \\\"80\\\" \\\"80\\\" \\\"43419\\\" \" +\n                \"\\\"128\\\" \\\"128\\\" \\\"140597540726848\\\" \\\"140597540726848\\\" \\\"\\\" \\\"GET /images/poweredby.png HTTP/1.1\\\" \" +\n                \"\\\"-\\\" \\\"304\\\" \\\"304\\\" \\\"[21/Nov/2014:15:48:21 +0000]\\\" \\\"1416584901091\\\" \\\"1416584901091\\\" \\\"14165849\" +\n                \"01091\\\" \\\"1416584901091601\\\" \\\"1416584901091601\\\" \\\"1416584901091870\\\" \\\"091\\\" \\\"091\\\" \\\"091\\\" \\\"0916\" +\n                \"01\\\" \\\"091601\\\" \\\"091870\\\" \\\"0\\\" \\\"-\\\" \\\"/images/poweredby.png\\\" \\\"172.17.0.2\\\" \\\"172.17.0.2\\\" \\\"+\\\" \" +\n                \"\\\"448\\\" \\\"179\\\" \\\"-\\\" \\\"-\\\" \\\"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) \" +\n                \"Chrome/38.0.2125.122 Safari/537.36\\\" \\\"http://172.17.0.2/\\\"\",\n        \"\\\"%\\\" \\\"172.17.42.1\\\" \\\"172.17.42.1\\\" \\\"172.17.0.2\\\" \\\"213\\\" \\\"213\\\" \\\"448\\\" \\\"/var/www/html/ladkshjfkjasdhf\" +\n                \"\\\" \\\"172.17.42.1\\\" \\\"HTTP/1.1\\\" \\\"0\\\" \\\"-\\\" \\\"-\\\" \\\"GET\\\" \\\"80\\\" \\\"80\\\" \\\"80\\\" \\\"43482\\\" \\\"136\\\" \\\"13\" +\n                \"6\\\" \\\"140597540726848\\\" \\\"140597540726848\\\" \\\"\\\" \\\"GET /ladkshjfkjasdhf HTTP/1.1\\\" \\\"-\\\" \\\"404\\\" \\\"40\" +\n                \"4\\\" \\\"[21/Nov/2014:15:50:45 +0000]\\\" \\\"1416585045231\\\" \\\"1416585045231\\\" \\\"1416585045231\\\" \\\"14165850\" +\n                \"45231085\\\" \\\"1416585045231085\\\" \\\"1416585045231533\\\" \\\"231\\\" \\\"231\\\" \\\"231\\\" \\\"231085\\\" \\\"231085\\\" \\\"\" +\n                \"231533\\\" \\\"0\\\" \\\"-\\\" \\\"/ladkshjfkjasdhf\\\" \\\"172.17.0.2\\\" \\\"172.17.0.2\\\" \\\"+\\\" \\\"356\\\" \\\"429\\\" \\\"-\\\" \" +\n                \"\\\"-\\\" \\\"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.122 S\" +\n                \"afari/537.36\\\" \\\"-\\\"\",\n    };\n\n\n    @Test\n    void testBasicParsing() throws Exception {\n        Parser<MyRecord> parser = new HttpdLoglineParser<>(MyRecord.class, LOG_FORMAT);\n        MyRecord         record = new MyRecord();\n\n        List<String> paths = parser.getPossiblePaths();\n\n        parser.addParseTarget(record.getClass().getMethod(\"setValue\", String.class, String.class), paths);\n\n        for (String logline : LOG_LINES) {\n            record.clear();\n            parser.parse(record, logline);\n            System.out.println(record);\n        }\n    }\n\n    @Test\n    void ensureAllOutputsAreThere() {\n        Parser<MyRecord> parser = new HttpdLoglineParser<>(MyRecord.class, LOG_FORMAT);\n        List<String> paths = parser.getPossiblePaths(15, true); // Get the list presorted\n\n        String pathString = paths\n            .stream()\n            .map(s -> String.format(\"%-30s: %s\", s.substring(0, s.indexOf(':')), s.substring(s.indexOf(':')+1)))\n            .collect(Collectors.joining(\"\\n\"));\n\n        String expectedPaths =\n            \"IP                            : connection.client.host.last\\n\" +\n            \"IP                            : connection.client.host\\n\" +\n            \"IP                            : connection.client.ip.last\\n\" +\n            \"IP                            : connection.client.ip\\n\" +\n            \"NUMBER                        : connection.client.logname.last\\n\" +\n            \"NUMBER                        : connection.client.logname\\n\" +\n            \"IP                            : connection.client.peerip.last\\n\" +\n            \"IP                            : connection.client.peerip\\n\" +\n            \"PORT                          : connection.client.port.last\\n\" +\n            \"PORT                          : connection.client.port\\n\" +\n            \"STRING                        : connection.client.user.last\\n\" +\n            \"STRING                        : connection.client.user\\n\" +\n            \"NUMBER                        : connection.keepalivecount.last\\n\" +\n            \"NUMBER                        : connection.keepalivecount\\n\" +\n            \"NUMBER                        : connection.server.child.hexthreadid.last\\n\" +\n            \"NUMBER                        : connection.server.child.hexthreadid\\n\" +\n            \"NUMBER                        : connection.server.child.processid.last\\n\" +\n            \"NUMBER                        : connection.server.child.processid\\n\" +\n            \"NUMBER                        : connection.server.child.threadid.last\\n\" +\n            \"NUMBER                        : connection.server.child.threadid\\n\" +\n            \"IP                            : connection.server.ip.last\\n\" +\n            \"IP                            : connection.server.ip\\n\" +\n            \"STRING                        : connection.server.name.canonical.last\\n\" +\n            \"STRING                        : connection.server.name.canonical\\n\" +\n            \"STRING                        : connection.server.name.last\\n\" +\n            \"STRING                        : connection.server.name\\n\" +\n            \"PORT                          : connection.server.port.canonical.last\\n\" +\n            \"PORT                          : connection.server.port.canonical\\n\" +\n            \"PORT                          : connection.server.port.last\\n\" +\n            \"PORT                          : connection.server.port\\n\" +\n            \"BYTES                         : request.bytes.last\\n\" +\n            \"BYTESCLF                      : request.bytes.last\\n\" +\n            \"BYTES                         : request.bytes\\n\" +\n            \"BYTESCLF                      : request.bytes\\n\" +\n            \"HTTP.COOKIE                   : request.cookies.*\\n\" +\n            \"HTTP.COOKIE                   : request.cookies.last.*\\n\" +\n            \"HTTP.COOKIES                  : request.cookies.last\\n\" +\n            \"HTTP.COOKIES                  : request.cookies\\n\" +\n            \"STRING                        : request.errorlogid.last\\n\" +\n            \"STRING                        : request.errorlogid\\n\" +\n            \"HTTP.METHOD                   : request.firstline.method\\n\" +\n            \"HTTP.METHOD                   : request.firstline.original.method\\n\" +\n            \"HTTP.PROTOCOL.VERSION         : request.firstline.original.protocol.version\\n\" +\n            \"HTTP.PROTOCOL                 : request.firstline.original.protocol\\n\" +\n            \"HTTP.PROTOCOL_VERSION         : request.firstline.original.protocol\\n\" +\n            \"HTTP.HOST                     : request.firstline.original.uri.host\\n\" +\n            \"HTTP.PATH                     : request.firstline.original.uri.path\\n\" +\n            \"HTTP.PORT                     : request.firstline.original.uri.port\\n\" +\n            \"HTTP.PROTOCOL                 : request.firstline.original.uri.protocol\\n\" +\n            \"STRING                        : request.firstline.original.uri.query.*\\n\" +\n            \"HTTP.QUERYSTRING              : request.firstline.original.uri.query\\n\" +\n            \"HTTP.REF                      : request.firstline.original.uri.ref\\n\" +\n            \"HTTP.USERINFO                 : request.firstline.original.uri.userinfo\\n\" +\n            \"HTTP.URI                      : request.firstline.original.uri\\n\" +\n            \"HTTP.FIRSTLINE                : request.firstline.original\\n\" +\n            \"HTTP.PROTOCOL.VERSION         : request.firstline.protocol.version\\n\" +\n            \"HTTP.PROTOCOL                 : request.firstline.protocol\\n\" +\n            \"HTTP.PROTOCOL_VERSION         : request.firstline.protocol\\n\" +\n            \"HTTP.HOST                     : request.firstline.uri.host\\n\" +\n            \"HTTP.PATH                     : request.firstline.uri.path\\n\" +\n            \"HTTP.PORT                     : request.firstline.uri.port\\n\" +\n            \"HTTP.PROTOCOL                 : request.firstline.uri.protocol\\n\" +\n            \"STRING                        : request.firstline.uri.query.*\\n\" +\n            \"HTTP.QUERYSTRING              : request.firstline.uri.query\\n\" +\n            \"HTTP.REF                      : request.firstline.uri.ref\\n\" +\n            \"HTTP.USERINFO                 : request.firstline.uri.userinfo\\n\" +\n            \"HTTP.URI                      : request.firstline.uri\\n\" +\n            \"HTTP.FIRSTLINE                : request.firstline\\n\" +\n            \"STRING                        : request.handler.last\\n\" +\n            \"STRING                        : request.handler\\n\" +\n            \"HTTP.METHOD                   : request.method.last\\n\" +\n            \"HTTP.METHOD                   : request.method\\n\" +\n            \"PROTOCOL                      : request.protocol.last\\n\" +\n            \"PROTOCOL                      : request.protocol\\n\" +\n            \"STRING                        : request.querystring.*\\n\" +\n            \"STRING                        : request.querystring.last.*\\n\" +\n            \"HTTP.QUERYSTRING              : request.querystring.last\\n\" +\n            \"HTTP.QUERYSTRING              : request.querystring\\n\" +\n            \"TIME.EPOCH                    : request.receive.time.begin.msec.last\\n\" +\n            \"TIME.EPOCH                    : request.receive.time.begin.msec\\n\" +\n            \"TIME.EPOCH                    : request.receive.time.begin.msec_frac.last\\n\" +\n            \"TIME.EPOCH                    : request.receive.time.begin.msec_frac\\n\" +\n            \"TIME.EPOCH.USEC               : request.receive.time.begin.usec.last\\n\" +\n            \"TIME.EPOCH.USEC               : request.receive.time.begin.usec\\n\" +\n            \"TIME.EPOCH.USEC_FRAC          : request.receive.time.begin.usec_frac.last\\n\" +\n            \"TIME.EPOCH.USEC_FRAC          : request.receive.time.begin.usec_frac\\n\" +\n            \"TIME.DATE                     : request.receive.time.date\\n\" +\n            \"TIME.DATE                     : request.receive.time.date_utc\\n\" +\n            \"TIME.DAY                      : request.receive.time.day\\n\" +\n            \"TIME.DAY                      : request.receive.time.day_utc\\n\" +\n            \"TIME.EPOCH                    : request.receive.time.end.msec.last\\n\" +\n            \"TIME.EPOCH                    : request.receive.time.end.msec\\n\" +\n            \"TIME.EPOCH                    : request.receive.time.end.msec_frac.last\\n\" +\n            \"TIME.EPOCH                    : request.receive.time.end.msec_frac\\n\" +\n            \"TIME.EPOCH.USEC               : request.receive.time.end.usec.last\\n\" +\n            \"TIME.EPOCH.USEC               : request.receive.time.end.usec\\n\" +\n            \"TIME.EPOCH.USEC_FRAC          : request.receive.time.end.usec_frac.last\\n\" +\n            \"TIME.EPOCH.USEC_FRAC          : request.receive.time.end.usec_frac\\n\" +\n            \"TIME.EPOCH                    : request.receive.time.epoch\\n\" +\n            \"TIME.HOUR                     : request.receive.time.hour\\n\" +\n            \"TIME.HOUR                     : request.receive.time.hour_utc\\n\" +\n            \"TIME.DATE                     : request.receive.time.last.date\\n\" +\n            \"TIME.DATE                     : request.receive.time.last.date_utc\\n\" +\n            \"TIME.DAY                      : request.receive.time.last.day\\n\" +\n            \"TIME.DAY                      : request.receive.time.last.day_utc\\n\" +\n            \"TIME.EPOCH                    : request.receive.time.last.epoch\\n\" +\n            \"TIME.HOUR                     : request.receive.time.last.hour\\n\" +\n            \"TIME.HOUR                     : request.receive.time.last.hour_utc\\n\" +\n            \"TIME.MICROSECOND              : request.receive.time.last.microsecond\\n\" +\n            \"TIME.MICROSECOND              : request.receive.time.last.microsecond_utc\\n\" +\n            \"TIME.MILLISECOND              : request.receive.time.last.millisecond\\n\" +\n            \"TIME.MILLISECOND              : request.receive.time.last.millisecond_utc\\n\" +\n            \"TIME.MINUTE                   : request.receive.time.last.minute\\n\" +\n            \"TIME.MINUTE                   : request.receive.time.last.minute_utc\\n\" +\n            \"TIME.MONTH                    : request.receive.time.last.month\\n\" +\n            \"TIME.MONTH                    : request.receive.time.last.month_utc\\n\" +\n            \"TIME.MONTHNAME                : request.receive.time.last.monthname\\n\" +\n            \"TIME.MONTHNAME                : request.receive.time.last.monthname_utc\\n\" +\n            \"TIME.NANOSECOND               : request.receive.time.last.nanosecond\\n\" +\n            \"TIME.NANOSECOND               : request.receive.time.last.nanosecond_utc\\n\" +\n            \"TIME.SECOND                   : request.receive.time.last.second\\n\" +\n            \"TIME.SECOND                   : request.receive.time.last.second_utc\\n\" +\n            \"TIME.TIME                     : request.receive.time.last.time\\n\" +\n            \"TIME.TIME                     : request.receive.time.last.time_utc\\n\" +\n            \"TIME.ZONE                     : request.receive.time.last.timezone\\n\" +\n            \"TIME.WEEK                     : request.receive.time.last.weekofweekyear\\n\" +\n            \"TIME.WEEK                     : request.receive.time.last.weekofweekyear_utc\\n\" +\n            \"TIME.YEAR                     : request.receive.time.last.weekyear\\n\" +\n            \"TIME.YEAR                     : request.receive.time.last.weekyear_utc\\n\" +\n            \"TIME.YEAR                     : request.receive.time.last.year\\n\" +\n            \"TIME.YEAR                     : request.receive.time.last.year_utc\\n\" +\n            \"TIME.STAMP                    : request.receive.time.last\\n\" +\n            \"TIME.MICROSECOND              : request.receive.time.microsecond\\n\" +\n            \"TIME.MICROSECOND              : request.receive.time.microsecond_utc\\n\" +\n            \"TIME.MILLISECOND              : request.receive.time.millisecond\\n\" +\n            \"TIME.MILLISECOND              : request.receive.time.millisecond_utc\\n\" +\n            \"TIME.MINUTE                   : request.receive.time.minute\\n\" +\n            \"TIME.MINUTE                   : request.receive.time.minute_utc\\n\" +\n            \"TIME.MONTH                    : request.receive.time.month\\n\" +\n            \"TIME.MONTH                    : request.receive.time.month_utc\\n\" +\n            \"TIME.MONTHNAME                : request.receive.time.monthname\\n\" +\n            \"TIME.MONTHNAME                : request.receive.time.monthname_utc\\n\" +\n            \"TIME.EPOCH                    : request.receive.time.msec.last\\n\" +\n            \"TIME.EPOCH                    : request.receive.time.msec\\n\" +\n            \"TIME.EPOCH                    : request.receive.time.msec_frac.last\\n\" +\n            \"TIME.EPOCH                    : request.receive.time.msec_frac\\n\" +\n            \"TIME.NANOSECOND               : request.receive.time.nanosecond\\n\" +\n            \"TIME.NANOSECOND               : request.receive.time.nanosecond_utc\\n\" +\n            \"TIME.SECOND                   : request.receive.time.second\\n\" +\n            \"TIME.SECOND                   : request.receive.time.second_utc\\n\" +\n            \"TIME.TIME                     : request.receive.time.time\\n\" +\n            \"TIME.TIME                     : request.receive.time.time_utc\\n\" +\n            \"TIME.ZONE                     : request.receive.time.timezone\\n\" +\n            \"TIME.EPOCH.USEC               : request.receive.time.usec.last\\n\" +\n            \"TIME.EPOCH.USEC               : request.receive.time.usec\\n\" +\n            \"TIME.EPOCH.USEC_FRAC          : request.receive.time.usec_frac.last\\n\" +\n            \"TIME.EPOCH.USEC_FRAC          : request.receive.time.usec_frac\\n\" +\n            \"TIME.WEEK                     : request.receive.time.weekofweekyear\\n\" +\n            \"TIME.WEEK                     : request.receive.time.weekofweekyear_utc\\n\" +\n            \"TIME.YEAR                     : request.receive.time.weekyear\\n\" +\n            \"TIME.YEAR                     : request.receive.time.weekyear_utc\\n\" +\n            \"TIME.YEAR                     : request.receive.time.year\\n\" +\n            \"TIME.YEAR                     : request.receive.time.year_utc\\n\" +\n            \"TIME.STAMP                    : request.receive.time\\n\" +\n            \"HTTP.HOST                     : request.referer.host\\n\" +\n            \"HTTP.HOST                     : request.referer.last.host\\n\" +\n            \"HTTP.PATH                     : request.referer.last.path\\n\" +\n            \"HTTP.PORT                     : request.referer.last.port\\n\" +\n            \"HTTP.PROTOCOL                 : request.referer.last.protocol\\n\" +\n            \"STRING                        : request.referer.last.query.*\\n\" +\n            \"HTTP.QUERYSTRING              : request.referer.last.query\\n\" +\n            \"HTTP.REF                      : request.referer.last.ref\\n\" +\n            \"HTTP.USERINFO                 : request.referer.last.userinfo\\n\" +\n            \"HTTP.URI                      : request.referer.last\\n\" +\n            \"HTTP.PATH                     : request.referer.path\\n\" +\n            \"HTTP.PORT                     : request.referer.port\\n\" +\n            \"HTTP.PROTOCOL                 : request.referer.protocol\\n\" +\n            \"STRING                        : request.referer.query.*\\n\" +\n            \"HTTP.QUERYSTRING              : request.referer.query\\n\" +\n            \"HTTP.REF                      : request.referer.ref\\n\" +\n            \"HTTP.USERINFO                 : request.referer.userinfo\\n\" +\n            \"HTTP.URI                      : request.referer\\n\" +\n            \"PORT                          : request.server.port.canonical.last\\n\" +\n            \"PORT                          : request.server.port.canonical\\n\" +\n            \"STRING                        : request.status.last\\n\" +\n            \"STRING                        : request.status.original\\n\" +\n            \"STRING                        : request.status\\n\" +\n            \"URI                           : request.urlpath.original\\n\" +\n            \"URI                           : request.urlpath\\n\" +\n            \"HTTP.USERAGENT                : request.user-agent.last\\n\" +\n            \"HTTP.USERAGENT                : request.user-agent\\n\" +\n            \"BYTES                         : response.body.bytes.last\\n\" +\n            \"BYTESCLF                      : response.body.bytes.last\\n\" +\n            \"BYTES                         : response.body.bytes\\n\" +\n            \"BYTESCLF                      : response.body.bytes\\n\" +\n            \"BYTES                         : response.body.bytesclf\\n\" +\n            \"BYTESCLF                      : response.body.bytesclf\\n\" +\n            \"BYTES                         : response.bytes.last\\n\" +\n            \"BYTESCLF                      : response.bytes.last\\n\" +\n            \"BYTES                         : response.bytes\\n\" +\n            \"BYTESCLF                      : response.bytes\\n\" +\n            \"HTTP.CONNECTSTATUS            : response.connection.status.last\\n\" +\n            \"HTTP.CONNECTSTATUS            : response.connection.status\\n\" +\n            \"STRING                        : response.cookies.*.comment\\n\" +\n            \"STRING                        : response.cookies.*.domain\\n\" +\n            \"STRING                        : response.cookies.*.expires\\n\" +\n            \"TIME.EPOCH                    : response.cookies.*.expires\\n\" +\n            \"STRING                        : response.cookies.*.path\\n\" +\n            \"STRING                        : response.cookies.*.value\\n\" +\n            \"HTTP.SETCOOKIE                : response.cookies.*\\n\" +\n            \"STRING                        : response.cookies.last.*.comment\\n\" +\n            \"STRING                        : response.cookies.last.*.domain\\n\" +\n            \"STRING                        : response.cookies.last.*.expires\\n\" +\n            \"TIME.EPOCH                    : response.cookies.last.*.expires\\n\" +\n            \"STRING                        : response.cookies.last.*.path\\n\" +\n            \"STRING                        : response.cookies.last.*.value\\n\" +\n            \"HTTP.SETCOOKIE                : response.cookies.last.*\\n\" +\n            \"HTTP.SETCOOKIES               : response.cookies.last\\n\" +\n            \"HTTP.SETCOOKIES               : response.cookies\\n\" +\n            \"MICROSECONDS                  : response.server.processing.time.original\\n\" +\n            \"SECONDS                       : response.server.processing.time.original\\n\" +\n            \"MICROSECONDS                  : response.server.processing.time\\n\" +\n            \"SECONDS                       : response.server.processing.time\\n\" +\n            \"FILENAME                      : server.filename.last\\n\" +\n            \"FILENAME                      : server.filename\\n\" +\n            \"MICROSECONDS                  : server.process.time\";\n\n        assertEquals(expectedPaths, pathString);\n    }\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/test/java/nl/basjes/parse/httpdlog/ClientHintsTest.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage nl.basjes.parse.httpdlog;\n\nimport nl.basjes.parse.core.test.DissectorTester;\nimport nl.basjes.parse.core.test.TestRecord;\nimport org.junit.jupiter.api.Test;\n\n\nclass ClientHintsTest {\n    @Test\n    void testClientHintsWithEscapedDoubleQuotes() {\n        String logFormat = \"%a %l %u %t \\\"%r\\\" %>s %b \\\"%{Referer}i\\\" \\\"%{User-Agent}i\\\" \\\"%{Sec-CH-UA}i\\\" \\\"%{Sec-CH-UA-Arch}i\\\" \\\"%{Sec-CH-UA-Bitness}i\\\" \\\"%{Sec-CH-UA-Full-Version}i\\\" \\\"%{Sec-CH-UA-Full-Version-List}i\\\" \\\"%{Sec-CH-UA-Mobile}i\\\" \\\"%{Sec-CH-UA-Model}i\\\" \\\"%{Sec-CH-UA-Platform}i\\\" \\\"%{Sec-CH-UA-Platform-Version}i\\\" \\\"%{Sec-CH-UA-WoW64}i\\\" %V\";\n\n        // 10.20.30.40 - - [03/May/2022:14:08:37 +0200] \"GET / HTTP/1.1\" 200 16165 \"https://github.com/nielsbasjes/yauaa\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.88 Safari/537.36\" \"\\\" Not A;Brand\\\";v=\\\"99\\\", \\\"Chromium\\\";v=\\\"100\\\", \\\"Google Chrome\\\";v=\\\"100\\\"\" \"\\\"x86\\\"\" \"\\\"64\\\"\" \"\\\"100.0.4896.88\\\"\" \"\\\" Not A;Brand\\\";v=\\\"99.0.0.0\\\", \\\"Chromium\\\";v=\\\"100.0.4896.88\\\", \\\"Google Chrome\\\";v=\\\"100.0.4896.88\\\"\" \"?0\" \"\\\"\\\"\" \"\\\"macOS\\\"\" \"\\\"12.2.1\\\"\" \"?0\" try.yauaa.basjes.nl\n\n        String testLine = \"10.20.30.40 - - [03/May/2022:14:08:37 +0200] \\\"GET / HTTP/1.1\\\" 200 16165 \\\"https://github.com/nielsbasjes/yauaa\\\" \\\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.88 Safari/537.36\\\" \\\"\\\\\\\" Not A;Brand\\\\\\\";v=\\\\\\\"99\\\\\\\", \\\\\\\"Chromium\\\\\\\";v=\\\\\\\"100\\\\\\\", \\\\\\\"Google Chrome\\\\\\\";v=\\\\\\\"100\\\\\\\"\\\" \\\"\\\\\\\"x86\\\\\\\"\\\" \\\"\\\\\\\"64\\\\\\\"\\\" \\\"\\\\\\\"100.0.4896.88\\\\\\\"\\\" \\\"\\\\\\\" Not A;Brand\\\\\\\";v=\\\\\\\"99.0.0.0\\\\\\\", \\\\\\\"Chromium\\\\\\\";v=\\\\\\\"100.0.4896.88\\\\\\\", \\\\\\\"Google Chrome\\\\\\\";v=\\\\\\\"100.0.4896.88\\\\\\\"\\\" \\\"?0\\\" \\\"\\\\\\\"\\\\\\\"\\\" \\\"\\\\\\\"macOS\\\\\\\"\\\" \\\"\\\\\\\"12.2.1\\\\\\\"\\\" \\\"?0\\\" try.yauaa.basjes.nl\";\n        DissectorTester.create()\n            .withParser(new HttpdLoglineParser<>(TestRecord.class, logFormat))\n            .withInput(testLine)\n//            .getPossible().forEach(System.out::println);\n\n            .expect(\"HTTP.USERAGENT:request.user-agent\",                        \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.88 Safari/537.36\")\n            .expect(\"HTTP.HEADER:request.header.sec-ch-ua\",                     \"\\\" Not A;Brand\\\";v=\\\"99\\\", \\\"Chromium\\\";v=\\\"100\\\", \\\"Google Chrome\\\";v=\\\"100\\\"\")\n            .expect(\"HTTP.HEADER:request.header.sec-ch-ua-arch\",                \"\\\"x86\\\"\")\n            .expect(\"HTTP.HEADER:request.header.sec-ch-ua-bitness\",             \"\\\"64\\\"\")\n            .expect(\"HTTP.HEADER:request.header.sec-ch-ua-full-version\",        \"\\\"100.0.4896.88\\\"\")\n            .expect(\"HTTP.HEADER:request.header.sec-ch-ua-full-version-list\",   \"\\\" Not A;Brand\\\";v=\\\"99.0.0.0\\\", \\\"Chromium\\\";v=\\\"100.0.4896.88\\\", \\\"Google Chrome\\\";v=\\\"100.0.4896.88\\\"\")\n            .expect(\"HTTP.HEADER:request.header.sec-ch-ua-mobile\",              \"?0\")\n            .expect(\"HTTP.HEADER:request.header.sec-ch-ua-model\",               \"\\\"\\\"\")\n            .expect(\"HTTP.HEADER:request.header.sec-ch-ua-platform\",            \"\\\"macOS\\\"\")\n            .expect(\"HTTP.HEADER:request.header.sec-ch-ua-platform-version\",    \"\\\"12.2.1\\\"\")\n            .expect(\"HTTP.HEADER:request.header.sec-ch-ua-wow64\",               \"?0\")\n\n            .checkExpectations();\n    }\n\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/test/java/nl/basjes/parse/httpdlog/CookiesTest.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage nl.basjes.parse.httpdlog;\n\nimport nl.basjes.parse.core.Field;\nimport nl.basjes.parse.core.Parser;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\nclass CookiesTest {\n\n    private static final class EmptyTestRecord {\n    }\n\n    public static class TestRecord {\n\n        private final Map<String, String> results     = new HashMap<>(32);\n        private final Map<String, Long>   longResults = new HashMap<>(32);\n\n        @SuppressWarnings({\"unused\"}) // Used via reflection\n        @Field({\n            \"HTTP.QUERYSTRING:request.firstline.uri.query\",\n            \"IP:connection.client.ip\",\n            \"NUMBER:connection.client.logname\",\n            \"STRING:connection.client.user\",\n            \"HTTP.URI:request.firstline.uri\",\n            \"STRING:request.status.last\",\n            \"BYTESCLF:response.body.bytes\",\n            \"HTTP.URI:request.referer\",\n            \"HTTP.USERAGENT:request.user-agent\",\n            \"TIME.STAMP:request.receive.time\",\n            \"TIME.EPOCH:request.receive.time.epoch\",\n            \"TIME.SECOND:request.receive.time.second\",\n            \"TIME.DAY:request.receive.time.day\",\n            \"TIME.HOUR:request.receive.time.hour\",\n            \"TIME.MONTH:request.receive.time.month\",\n            \"TIME.YEAR:request.receive.time.year\",\n            \"TIME.MONTHNAME:request.receive.time.monthname\",\n            \"TIME.SECOND:request.receive.time.second_utc\",\n            \"TIME.DAY:request.receive.time.day_utc\",\n            \"TIME.HOUR:request.receive.time.hour_utc\",\n            \"TIME.MONTH:request.receive.time.month_utc\",\n            \"TIME.YEAR:request.receive.time.year_utc\",\n            \"TIME.MONTHNAME:request.receive.time.monthname_utc\",\n            \"MICROSECONDS:response.server.processing.time\",\n            \"STRING:request.status.last\",\n            \"HTTP.HEADER:response.header.etag\",\n\n            // Cookies\n            \"HTTP.COOKIES:request.cookies\",\n//            \"HTTP.COOKIE:request.cookies.apache\" ,\n//            \"HTTP.COOKIE:request.cookies.jquery-ui-theme\",\n            \"HTTP.COOKIE:request.cookies.*\",\n            \"HTTP.SETCOOKIES:response.cookies\",\n            \"HTTP.SETCOOKIE:response.cookies.nba-4\",\n            \"STRING:response.cookies.nba-4.value\",\n            \"STRING:response.cookies.nba-4.expires\",\n            \"STRING:response.cookies.nba-4.path\",\n            \"STRING:response.cookies.nba-4.domain\"\n            })\n        public void setValue(final String name, final String value) {\n            results.put(name, value);\n        }\n\n        public Map<String, String> getResults() {\n            return results;\n        }\n\n        @SuppressWarnings({\"unused\"}) // Used via reflection\n        @Field({\n            \"BYTESCLF:response.body.bytes\",\n            \"TIME.DAY:request.receive.time.day\",\n            \"TIME.HOUR:request.receive.time.hour\",\n            \"TIME.SECOND:request.receive.time.second\",\n            \"TIME.DAY:request.receive.time.day_utc\",\n            \"TIME.HOUR:request.receive.time.hour_utc\",\n            \"TIME.SECOND:request.receive.time.second_utc\",\n            \"TIME.EPOCH:request.receive.time.epoch\",\n        })\n        public void setValueLong(final String name, final Long value) {\n            longResults.put(name, value);\n        }\n\n        public Map<String, Long> getLongResults() {\n            return longResults;\n        }\n\n    }\n\n    private static final String LOG_FORMAT = \"%h %a %A %l %u %t \\\"%r\\\" \" +\n            \"%>s %b %p \\\"%q\\\" \\\"%{Referer}i\\\" %D \\\"%{User-agent}i\\\" \" +\n            \"\\\"%{Cookie}i\\\" \" +\n            \"\\\"%{Set-Cookie}o\\\" \" +\n            \"\\\"%{If-None-Match}i\\\" \\\"%{Etag}o\\\"\";\n\n    private static final String COOKIES_LINE =\n            \"127.0.0.1 127.0.0.1 127.0.0.1 - - [31/Dec/2012:23:00:44 -0700] \\\"GET /index.php HTTP/1.1\\\" \" +\n            \"200 - 80 \\\"\\\" \\\"-\\\" 80991 \\\"Mozilla/5.0 (X11; Linux i686 on x86_64; rv:11.0) Gecko/20100101 Firefox/11.0\\\" \" +\n            \"\\\"jquery-ui-theme=Eggplant; Apache=127.0.0.1.1351111543699529\\\" \" +\n            \"\\\"\" +\n                \"NBA-0=, \" +\n                \"NBA-1=1111, \" +\n                \"NBA-2=2222; expires=Wed, 01-Jan-2020 00:00:12 GMT, \" +\n                \"NBA-3=3333; expires=Wed, 01-Jan-2020 00:00:13 GMT; path=/, \" +\n                \"NBA-4=4444; expires=Wed, 01-Jan-2020 00:00:14 GMT; path=/; domain=.basj.es\" +\n            \"\\\" \\\"-\\\" \\\"-\\\"\";\n\n    // ----------------------------\n\n    @Test\n    void testEmptyRecordPossibles() {\n        Parser<EmptyTestRecord> parser = new HttpdLoglineParser<>(EmptyTestRecord.class, LOG_FORMAT);\n\n        List<String> possibles = parser.getPossiblePaths();\n        for (String possible : possibles) {\n            System.out.println(possible);\n        }\n    }\n\n    // ---------------\n\n    @Test\n    void testRecordPossibles() {\n        Parser<TestRecord> parser = new HttpdLoglineParser<>(TestRecord.class, LOG_FORMAT);\n\n        List<String> possibles = parser.getPossiblePaths();\n        for (String possible : possibles) {\n            System.out.println(possible);\n        }\n    }\n\n    // ---------------\n\n    private void check(String expect, Map<String, String> results, String parameter) {\n        assertEquals(expect, results.get(parameter));\n    }\n\n\n    @Test\n    void cookiesTest() throws Exception {\n\n        Parser<TestRecord> parser = new HttpdLoglineParser<>(TestRecord.class, LOG_FORMAT);\n\n        TestRecord record = new TestRecord();\n        parser.parse(record, COOKIES_LINE);\n\n        // ---------------\n\n        Map<String, String> results = record.getResults();\n        Map<String, Long> longResults = record.getLongResults();\n\n        // System.out.println(results.toString());\n\n        assertEquals(null, results.get(\"QUERYSTRING:request.firstline.uri.query.foo\"));\n        assertEquals(\"127.0.0.1\", results.get(\"IP:connection.client.ip\"));\n        assertEquals(null, results.get(\"NUMBER:connection.client.logname\"));\n        assertEquals(null, results.get(\"STRING:connection.client.user\"));\n        assertEquals(\"31/Dec/2012:23:00:44 -0700\", results.get(\"TIME.STAMP:request.receive.time\"));\n        assertEquals(\"1357020044000\", results.get(\"TIME.EPOCH:request.receive.time.epoch\"));\n        assertEquals(Long.valueOf(1357020044000L), longResults.get(\"TIME.EPOCH:request.receive.time.epoch\"));\n\n        assertEquals(\"2012\", results.get(\"TIME.YEAR:request.receive.time.year\"));\n        assertEquals(\"12\", results.get(\"TIME.MONTH:request.receive.time.month\"));\n        assertEquals(\"December\", results.get(\"TIME.MONTHNAME:request.receive.time.monthname\"));\n        assertEquals(\"31\", results.get(\"TIME.DAY:request.receive.time.day\"));\n        assertEquals(Long.valueOf(31), longResults.get(\"TIME.DAY:request.receive.time.day\"));\n        assertEquals(\"23\", results.get(\"TIME.HOUR:request.receive.time.hour\"));\n        assertEquals(Long.valueOf(23), longResults.get(\"TIME.HOUR:request.receive.time.hour\"));\n        assertEquals(\"44\", results.get(\"TIME.SECOND:request.receive.time.second\"));\n        assertEquals(Long.valueOf(44), longResults.get(\"TIME.SECOND:request.receive.time.second\"));\n\n        assertEquals(\"2013\", results.get(\"TIME.YEAR:request.receive.time.year_utc\"));\n        assertEquals(\"1\", results.get(\"TIME.MONTH:request.receive.time.month_utc\"));\n        assertEquals(\"January\", results.get(\"TIME.MONTHNAME:request.receive.time.monthname_utc\"));\n        assertEquals(\"1\", results.get(\"TIME.DAY:request.receive.time.day_utc\"));\n        assertEquals(Long.valueOf(1), longResults.get(\"TIME.DAY:request.receive.time.day_utc\"));\n        assertEquals(\"6\", results.get(\"TIME.HOUR:request.receive.time.hour_utc\"));\n        assertEquals(Long.valueOf(6), longResults.get(\"TIME.HOUR:request.receive.time.hour_utc\"));\n        assertEquals(\"44\", results.get(\"TIME.SECOND:request.receive.time.second_utc\"));\n        assertEquals(Long.valueOf(44), longResults.get(\"TIME.SECOND:request.receive.time.second_utc\"));\n\n\n        assertEquals(\"/index.php\", results.get(\"HTTP.URI:request.firstline.uri\"));\n        assertEquals(\"200\", results.get(\"STRING:request.status.last\"));\n\n        // The \"-\" value means \"Not specified\" which is mapped to the setter being called\n        // with a 'null' value intending to say \"We know it is not there\".\n        assertTrue(results.containsKey(\"BYTESCLF:response.body.bytes\"));\n        assertEquals(null, results.get(\"BYTESCLF:response.body.bytes\"));\n        assertTrue(longResults.containsKey(\"BYTESCLF:response.body.bytes\"));\n        assertEquals(null, longResults.get(\"BYTESCLF:response.body.bytes\"));\n\n        assertEquals(null, results.get(\"HTTP.URI:request.referer\"));\n        assertEquals(\"Mozilla/5.0 (X11; Linux i686 on x86_64; rv:11.0) Gecko/20100101 Firefox/11.0\",\n                     results.get(\"HTTP.USERAGENT:request.user-agent\"));\n        assertEquals(\"80991\", results.get(\"MICROSECONDS:response.server.processing.time\"));\n        assertEquals(null, results.get(\"HTTP.HEADER:response.header.etag\"));\n\n        assertEquals(\"Eggplant\", results.get(\"HTTP.COOKIE:request.cookies.jquery-ui-theme\"));\n        assertEquals(\"127.0.0.1.1351111543699529\", results.get(\"HTTP.COOKIE:request.cookies.apache\"));\n        assertEquals(\"NBA-0=, \" +\n                \"NBA-1=1111, \" +\n                \"NBA-2=2222; expires=Wed, 01-Jan-2020 00:00:12 GMT, \" +\n                \"NBA-3=3333; expires=Wed, 01-Jan-2020 00:00:13 GMT; path=/, \" +\n                \"NBA-4=4444; expires=Wed, 01-Jan-2020 00:00:14 GMT; path=/; domain=.basj.es\",\n                results.get(\"HTTP.SETCOOKIES:response.cookies\"));\n        assertEquals(\"NBA-4=4444; expires=Wed, 01-Jan-2020 00:00:14 GMT; path=/; domain=.basj.es\",\n                results.get(\"HTTP.SETCOOKIE:response.cookies.nba-4\"));\n        assertEquals(\"4444\", results.get(\"STRING:response.cookies.nba-4.value\"));\n\n        // The returned value may be off by 1 or 2 seconds due to rounding.\n        assertEquals(1577836814D, Double.parseDouble(results.get(\"STRING:response.cookies.nba-4.expires\")), 2D);\n        assertEquals(\"/\", results.get(\"STRING:response.cookies.nba-4.path\"));\n        assertEquals(\".basj.es\", results.get(\"STRING:response.cookies.nba-4.domain\"));\n    }\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/test/java/nl/basjes/parse/httpdlog/EdgeCasesTest.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage nl.basjes.parse.httpdlog;\n\nimport nl.basjes.parse.core.test.DissectorTester;\nimport nl.basjes.parse.core.test.TestRecord;\nimport org.junit.jupiter.api.Test;\n\nimport static org.junit.jupiter.api.Assertions.assertDoesNotThrow;\n\nclass EdgeCasesTest {\n    @Test\n    void testInvalidFirstLine() {\n        String logFormat = \"%a %{Host}i %u %t \\\"%r\\\" %>s %O \\\"%{Referer}i\\\" \\\"%{User-Agent}i\\\" %{Content-length}i %P %A\";\n        String testLine = \"1.2.3.4 - - [03/Apr/2017:03:27:28 -0600] \\\"\\\\x16\\\\x03\\\\x01\\\" 404 419 \\\"-\\\" \\\"-\\\" - 115052 5.6.7.8\";\n        DissectorTester.create()\n            .withParser(new HttpdLoglineParser<>(TestRecord.class, logFormat))\n            .withInput(testLine)\n\n            .expect(\"IP:connection.client.ip\",                      \"1.2.3.4\")\n            .expect(\"IP:connection.server.ip\",                      \"5.6.7.8\")\n            .expect(\"TIME.EPOCH:request.receive.time.last.epoch\",   1491211648000L)\n            .expect(\"STRING:connection.client.user\",                (String)null) // Is present AND is null\n\n            .expect(\"TIME.STAMP:request.receive.time.last\",         \"03/Apr/2017:03:27:28 -0600\")\n            .expect(\"TIME.DATE:request.receive.time.last.date\",     \"2017-04-03\")\n            .expect(\"TIME.TIME:request.receive.time.last.time\",     \"03:27:28\")\n\n            .expect(\"NUMBER:connection.server.child.processid\",     \"115052\")\n            .expect(\"BYTES:response.bytes\",                         \"419\")\n            .expect(\"STRING:request.status.last\",                   \"404\")\n            .expectNull(\"HTTP.USERAGENT:request.user-agent\")\n            .expectNull(\"HTTP.HEADER:request.header.host\")\n            .expectNull(\"HTTP.HEADER:request.header.content-length\")\n            .expectNull(\"HTTP.URI:request.referer\")\n\n            // This thing should be unparsable\n            .expect(\"HTTP.FIRSTLINE:request.firstline\",             \"\\u0016\\u0003\\u0001\")\n            .expectAbsentString(\"HTTP.METHOD:request.firstline.method\")\n            .expectAbsentString(\"HTTP.URI:request.firstline.uri\")\n            .expectAbsentString(\"HTTP.PROTOCOL:request.firstline.protocol\")\n\n            .checkExpectations();\n    }\n\n    void checkBadUri(String logLine, String expectedUri) {\n        String logFormat = \"common\";\n        assertDoesNotThrow(() -> {\n            DissectorTester.create()\n                .withParser(new HttpdLoglineParser<>(TestRecord.class, logFormat))\n                .withInput(logLine)\n                .expect(\"HTTP.URI:request.firstline.uri\", expectedUri)\n                .expectAbsentString(\"HTTP.HOST:request.firstline.uri.host\")\n                .expectAbsentString(\"HTTP.PATH:request.firstline.uri.path\")\n                .expectAbsentString(\"HTTP.PORT:request.firstline.uri.port\")\n                .expectAbsentString(\"HTTP.PROTOCOL:request.firstline.uri.protocol\")\n                .expectAbsentString(\"HTTP.QUERYSTRING:request.firstline.uri.query\")\n                .expectAbsentString(\"HTTP.REF:request.firstline.uri.ref\")\n                .expectAbsentString(\"HTTP.USERINFO:request.firstline.uri.userinfo\")\n                .printAllPossibleValues();\n        });\n    }\n\n    @Test\n    void testBadUri() {\n        checkBadUri(\n            \"10.10.10.10 - - [28/Feb/2023:16:48:52 +0800] \\\"GET :@bxss.me::80/rpb.png HTTP/1.1\\\" 400 1160\",\n            \":@bxss.me::80/rpb.png\");\n\n        checkBadUri(\n            \"10.10.10.10 - - [28/Feb/2023:16:48:51 +0800] \\\"GET @bxss.me::80/rpb.png HTTP/1.1\\\" 400 1160\",\n            \"@bxss.me::80/rpb.png\");\n\n        checkBadUri(\n            \"10.10.10.10 - - [28/Feb/2023:16:48:51 +0800] \\\"GET :@bxss.me/rpb.png HTTP/1.1\\\" 400 1160\",\n            \":@bxss.me/rpb.png\");\n    }\n\n    @Test\n    void checkErrorLogging(){\n        HttpdLogFormatDissector dissector = new HttpdLogFormatDissector();\n        dissector\n            // Apache format\n            .addLogFormat(\"%t\")\n            .addMultipleLogFormats(\"%a\\n%b\\n%c\")\n            .addLogFormat(\"%b\") // Already have this one\n            // Nginx format\n            .addLogFormat(\"$remote_addr\")\n            .addMultipleLogFormats(\"$time_local\\n$body_bytes_sent\\n$status\")\n            .addLogFormat(\"$body_bytes_sent\") // Already have this one\n            // Unable to determine\n            .addLogFormat(\"blup\");\n    }\n\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/test/java/nl/basjes/parse/httpdlog/JettyLogFormatParserTest.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage nl.basjes.parse.httpdlog;\n\nimport nl.basjes.parse.core.Field;\nimport nl.basjes.parse.core.Parser;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\nclass JettyLogFormatParserTest {\n\n    // ------------------------------------------\n\n    public static class TestRecord {\n        private final Map<String, String> results = new HashMap<>(32);\n\n        @SuppressWarnings(\"UnusedDeclaration\")\n        @Field({\n            \"IP:connection.client.host\",\n            \"NUMBER:connection.client.logname\",\n            \"STRING:connection.client.user\",\n            \"TIME.STAMP:request.receive.time\",\n            \"TIME.DAY:request.receive.time.day\",\n            \"HTTP.FIRSTLINE:request.firstline\",\n            \"STRING:request.status.last\",\n            \"BYTES:response.body.bytes\",\n            \"HTTP.URI:request.referer\",\n            \"HTTP.USERAGENT:request.user-agent\",\n            \"MICROSECONDS:response.server.processing.time\"\n        })\n        public void setValue(final String name, final String value) {\n            results.put(name, value);\n        }\n\n        public Map<String, String> getResults() {\n            return results;\n        }\n    }\n\n    // ------------------------------------------\n\n    @Test\n    void buggyJettyLogline() throws Exception {\n        // In Jetty\n        // - an extra space is included if the useragent is absent (the >\"-\"  < near the end).\n        // - two extra spaces are included if the user field is absent ( \" - \" instead of \"-\" )\n        String[] lines = {\n            \"0.0.0.0 - x [24/Jul/2016:07:08:31 +0000] \\\"GET http://[:1]/foo HTTP/1.1\\\" 400 0 \\\"http://other.site\\\" \\\"-\\\"  8\",\n            \"0.0.0.0 -  -  [24/Jul/2016:07:08:31 +0000] \\\"GET http://[:1]/foo HTTP/1.1\\\" 400 0 \\\"http://other.site\\\" \\\"-\\\"  8\",\n            \"0.0.0.0 - x [24/Jul/2016:07:08:31 +0000] \\\"GET http://[:1]/foo HTTP/1.1\\\" 400 0 \\\"http://other.site\\\" \\\"Mozilla/5.0 (dummy)\\\" 8\",\n            \"0.0.0.0 -  -  [24/Jul/2016:07:08:31 +0000] \\\"GET http://[:1]/foo HTTP/1.1\\\" 400 0 \\\"http://other.site\\\" \\\"Mozilla/5.0 (dummy)\\\" 8\",\n        };\n\n        Parser<TestRecord> parser = new HttpdLoglineParser<>(TestRecord.class,\n            \"ENABLE JETTY FIX\\n\"+\n            \"%h %l %u %t \\\"%r\\\" %>s %b \\\"%{Referer}i\\\" \\\"%{User-Agent}i\\\" %D\"\n        );\n\n        for (String line:lines) {\n            TestRecord record = new TestRecord();\n            parser.parse(record, line);\n            Map<String, String> results = record.getResults();\n\n            assertEquals(\"0.0.0.0\", results.get(\"IP:connection.client.host\"));\n            assertEquals(null, results.get(\"NUMBER:connection.client.logname\"));\n\n            String user = results.get(\"STRING:connection.client.user\");\n            if (user!=null) {\n                assertEquals(\"x\", user);\n            }\n            assertEquals(\"24/Jul/2016:07:08:31 +0000\", results.get(\"TIME.STAMP:request.receive.time\"));\n            assertEquals(\"24\", results.get(\"TIME.DAY:request.receive.time.day\"));\n            assertEquals(\"GET http://[:1]/foo HTTP/1.1\", results.get(\"HTTP.FIRSTLINE:request.firstline\"));\n            assertEquals(\"400\", results.get(\"STRING:request.status.last\"));\n            assertEquals(\"0\", results.get(\"BYTES:response.body.bytes\"));\n            assertEquals(\"http://other.site\", results.get(\"HTTP.URI:request.referer\"));\n\n            String useragent = results.get(\"HTTP.USERAGENT:request.user-agent\");\n            if (useragent!=null) {\n                assertEquals(\"Mozilla/5.0 (dummy)\", useragent);\n            }\n            assertEquals(\"8\", results.get(\"MICROSECONDS:response.server.processing.time\"));\n\n        }\n    }\n\n    // -----------------------------------------\n\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/test/java/nl/basjes/parse/httpdlog/JsonLogFormatTest.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage nl.basjes.parse.httpdlog;\n\nimport nl.basjes.parse.core.Parser;\nimport nl.basjes.parse.core.test.DissectorTester;\nimport nl.basjes.parse.core.test.TestRecord;\nimport org.junit.jupiter.api.Test;\n\n\n// CHECKSTYLE.OFF: LineLength\nclass JsonLogFormatTest {\n    // As seen here: http://mail-archives.apache.org/mod_mbox/kafka-users/201408.mbox/%3C1407447350019.7022@roomkey.com%3E\n    // LogFormat \"{\\\"@timestamp\\\":\\\"%{%Y-%m-%dT%H:%M:%S%z}t\\\",\\\"mod_proxy\\\":{\\\"x-forwarded-for\\\":\\\"%{X-Forwarded-For}i\\\"},\\\"mod_headers\\\":{\\\"referer\\\":\\\"%{Referer}i\\\",\\\"user-agent\\\":\\\"%{User-Agent}i\\\",\\\"host\\\":\\\"%{Host}i\\\"},\\\"mod_log\\\":{\\\"server_name\\\":\\\"%V\\\",\\\"remote_logname\\\":\\\"%l\\\",\\\"remote_user\\\":\\\"%u\\\",\\\"first_request\\\":\\\"%r\\\",\\\"last_request_status\\\":\\\"%>s\\\",\\\"response_size_bytes\\\":%B,\\\"duration_usec\\\":%D,\\\"@version\\\":1 }\" logstash_json\n\n    private static final String LOGFORMAT = \"{\\\"@timestamp\\\":\\\"%{%Y-%m-%dT%H:%M:%S %z}t\\\",\\\"mod_proxy\\\":{\\\"x-forwarded-for\\\":\\\"%{X-Forwarded-For}i\\\"},\\\"mod_headers\\\":{\\\"referer\\\":\\\"%{Referer}i\\\",\\\"user-agent\\\":\\\"%{User-Agent}i\\\",\\\"host\\\":\\\"%{Host}i\\\"},\\\"mod_log\\\":{\\\"server_name\\\":\\\"%V\\\",\\\"remote_logname\\\":\\\"%l\\\",\\\"remote_user\\\":\\\"%u\\\",\\\"first_request\\\":\\\"%r\\\",\\\"last_request_status\\\":\\\"%>s\\\",\\\"response_size_bytes\\\":%B,\\\"duration_usec\\\":%D,\\\"@version\\\":1 }\";\n\n    private static final String[] LOGLINES = {\n        \"{\\\"@timestamp\\\":\\\"2015-11-25T15:24:45 +0100\\\",\\\"mod_proxy\\\":{\\\"x-forwarded-for\\\":\\\"-\\\"},\\\"mod_headers\\\":{\\\"referer\\\":\\\"-\\\",\\\"user-agent\\\":\\\"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.86 Safari/537.36\\\",\\\"host\\\":\\\"localhost\\\"},\\\"mod_log\\\":{\\\"server_name\\\":\\\"localhost\\\",\\\"remote_logname\\\":\\\"-\\\",\\\"remote_user\\\":\\\"-\\\",\\\"first_request\\\":\\\"GET / HTTP/1.1\\\",\\\"last_request_status\\\":\\\"403\\\",\\\"response_size_bytes\\\":4897,\\\"duration_usec\\\":909,\\\"@version\\\":1 }\",\n        \"{\\\"@timestamp\\\":\\\"2015-11-25T15:24:45 +0100\\\",\\\"mod_proxy\\\":{\\\"x-forwarded-for\\\":\\\"-\\\"},\\\"mod_headers\\\":{\\\"referer\\\":\\\"http://localhost/\\\",\\\"user-agent\\\":\\\"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.86 Safari/537.36\\\",\\\"host\\\":\\\"localhost\\\"},\\\"mod_log\\\":{\\\"server_name\\\":\\\"localhost\\\",\\\"remote_logname\\\":\\\"-\\\",\\\"remote_user\\\":\\\"-\\\",\\\"first_request\\\":\\\"GET /noindex/css/bootstrap.min.css HTTP/1.1\\\",\\\"last_request_status\\\":\\\"200\\\",\\\"response_size_bytes\\\":19341,\\\"duration_usec\\\":657,\\\"@version\\\":1 }\",\n        \"{\\\"@timestamp\\\":\\\"2015-11-25T15:24:45 +0100\\\",\\\"mod_proxy\\\":{\\\"x-forwarded-for\\\":\\\"-\\\"},\\\"mod_headers\\\":{\\\"referer\\\":\\\"http://localhost/\\\",\\\"user-agent\\\":\\\"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.86 Safari/537.36\\\",\\\"host\\\":\\\"localhost\\\"},\\\"mod_log\\\":{\\\"server_name\\\":\\\"localhost\\\",\\\"remote_logname\\\":\\\"-\\\",\\\"remote_user\\\":\\\"-\\\",\\\"first_request\\\":\\\"GET /noindex/css/open-sans.css HTTP/1.1\\\",\\\"last_request_status\\\":\\\"200\\\",\\\"response_size_bytes\\\":5081,\\\"duration_usec\\\":680,\\\"@version\\\":1 }\",\n        \"{\\\"@timestamp\\\":\\\"2015-11-25T15:24:45 +0100\\\",\\\"mod_proxy\\\":{\\\"x-forwarded-for\\\":\\\"-\\\"},\\\"mod_headers\\\":{\\\"referer\\\":\\\"http://localhost/\\\",\\\"user-agent\\\":\\\"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.86 Safari/537.36\\\",\\\"host\\\":\\\"localhost\\\"},\\\"mod_log\\\":{\\\"server_name\\\":\\\"localhost\\\",\\\"remote_logname\\\":\\\"-\\\",\\\"remote_user\\\":\\\"-\\\",\\\"first_request\\\":\\\"GET /images/apache_pb.gif HTTP/1.1\\\",\\\"last_request_status\\\":\\\"200\\\",\\\"response_size_bytes\\\":2326,\\\"duration_usec\\\":728,\\\"@version\\\":1 }\",\n        \"{\\\"@timestamp\\\":\\\"2015-11-25T15:24:45 +0100\\\",\\\"mod_proxy\\\":{\\\"x-forwarded-for\\\":\\\"-\\\"},\\\"mod_headers\\\":{\\\"referer\\\":\\\"http://localhost/\\\",\\\"user-agent\\\":\\\"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.86 Safari/537.36\\\",\\\"host\\\":\\\"localhost\\\"},\\\"mod_log\\\":{\\\"server_name\\\":\\\"localhost\\\",\\\"remote_logname\\\":\\\"-\\\",\\\"remote_user\\\":\\\"-\\\",\\\"first_request\\\":\\\"GET /images/poweredby.png HTTP/1.1\\\",\\\"last_request_status\\\":\\\"200\\\",\\\"response_size_bytes\\\":3956,\\\"duration_usec\\\":498,\\\"@version\\\":1 }\",\n        \"{\\\"@timestamp\\\":\\\"2015-11-25T15:24:45 +0100\\\",\\\"mod_proxy\\\":{\\\"x-forwarded-for\\\":\\\"-\\\"},\\\"mod_headers\\\":{\\\"referer\\\":\\\"http://localhost/noindex/css/open-sans.css\\\",\\\"user-agent\\\":\\\"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.86 Safari/537.36\\\",\\\"host\\\":\\\"localhost\\\"},\\\"mod_log\\\":{\\\"server_name\\\":\\\"localhost\\\",\\\"remote_logname\\\":\\\"-\\\",\\\"remote_user\\\":\\\"-\\\",\\\"first_request\\\":\\\"GET /noindex/css/fonts/Light/OpenSans-Light.woff HTTP/1.1\\\",\\\"last_request_status\\\":\\\"404\\\",\\\"response_size_bytes\\\":241,\\\"duration_usec\\\":147,\\\"@version\\\":1 }\",\n        \"{\\\"@timestamp\\\":\\\"2015-11-25T15:24:45 +0100\\\",\\\"mod_proxy\\\":{\\\"x-forwarded-for\\\":\\\"-\\\"},\\\"mod_headers\\\":{\\\"referer\\\":\\\"http://localhost/noindex/css/open-sans.css\\\",\\\"user-agent\\\":\\\"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.86 Safari/537.36\\\",\\\"host\\\":\\\"localhost\\\"},\\\"mod_log\\\":{\\\"server_name\\\":\\\"localhost\\\",\\\"remote_logname\\\":\\\"-\\\",\\\"remote_user\\\":\\\"-\\\",\\\"first_request\\\":\\\"GET /noindex/css/fonts/Bold/OpenSans-Bold.woff HTTP/1.1\\\",\\\"last_request_status\\\":\\\"404\\\",\\\"response_size_bytes\\\":239,\\\"duration_usec\\\":536,\\\"@version\\\":1 }\",\n        \"{\\\"@timestamp\\\":\\\"2015-11-25T15:24:45 +0100\\\",\\\"mod_proxy\\\":{\\\"x-forwarded-for\\\":\\\"-\\\"},\\\"mod_headers\\\":{\\\"referer\\\":\\\"http://localhost/noindex/css/open-sans.css\\\",\\\"user-agent\\\":\\\"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.86 Safari/537.36\\\",\\\"host\\\":\\\"localhost\\\"},\\\"mod_log\\\":{\\\"server_name\\\":\\\"localhost\\\",\\\"remote_logname\\\":\\\"-\\\",\\\"remote_user\\\":\\\"-\\\",\\\"first_request\\\":\\\"GET /noindex/css/fonts/Bold/OpenSans-Bold.ttf HTTP/1.1\\\",\\\"last_request_status\\\":\\\"404\\\",\\\"response_size_bytes\\\":238,\\\"duration_usec\\\":347,\\\"@version\\\":1 }\",\n        \"{\\\"@timestamp\\\":\\\"2015-11-25T15:24:45 +0100\\\",\\\"mod_proxy\\\":{\\\"x-forwarded-for\\\":\\\"-\\\"},\\\"mod_headers\\\":{\\\"referer\\\":\\\"http://localhost/noindex/css/open-sans.css\\\",\\\"user-agent\\\":\\\"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.86 Safari/537.36\\\",\\\"host\\\":\\\"localhost\\\"},\\\"mod_log\\\":{\\\"server_name\\\":\\\"localhost\\\",\\\"remote_logname\\\":\\\"-\\\",\\\"remote_user\\\":\\\"-\\\",\\\"first_request\\\":\\\"GET /noindex/css/fonts/Light/OpenSans-Light.ttf HTTP/1.1\\\",\\\"last_request_status\\\":\\\"404\\\",\\\"response_size_bytes\\\":240,\\\"duration_usec\\\":268,\\\"@version\\\":1 }\",\n        \"{\\\"@timestamp\\\":\\\"2015-11-25T15:24:45 +0100\\\",\\\"mod_proxy\\\":{\\\"x-forwarded-for\\\":\\\"-\\\"},\\\"mod_headers\\\":{\\\"referer\\\":\\\"http://localhost/\\\",\\\"user-agent\\\":\\\"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.86 Safari/537.36\\\",\\\"host\\\":\\\"localhost\\\"},\\\"mod_log\\\":{\\\"server_name\\\":\\\"localhost\\\",\\\"remote_logname\\\":\\\"-\\\",\\\"remote_user\\\":\\\"-\\\",\\\"first_request\\\":\\\"GET /favicon.ico HTTP/1.1\\\",\\\"last_request_status\\\":\\\"404\\\",\\\"response_size_bytes\\\":209,\\\"duration_usec\\\":342,\\\"@version\\\":1 }\",\n    };\n\n    @Test\n    void testBasicParsing() {\n        Parser<TestRecord> parser = new HttpdLoglineParser<>(TestRecord.class, LOGFORMAT);\n\n        DissectorTester tester = DissectorTester.create()\n            .withParser(parser)\n            .verbose();\n\n        for (String logline: LOGLINES){\n            tester.withInput(logline);\n        }\n\n        tester.expectValuePresent(\"TIME.LOCALIZEDSTRING:request.receive.time\")\n              .expectValuePresent(\"STRING:connection.server.name\")\n              .expectValuePresent(\"NUMBER:connection.client.logname\")\n              .expectNull(\"STRING:connection.client.user\")\n              .expectValuePresent(\"HTTP.HEADER:request.header.x-forwarded-for\")\n              .expectValuePresent(\"HTTP.URI:request.referer\")\n              .expectValuePresent(\"HTTP.USERAGENT:request.user-agent\")\n              .expectValuePresent(\"HTTP.HEADER:request.header.host\")\n              .expectValuePresent(\"HTTP.FIRSTLINE:request.firstline\")\n              .expectValuePresent(\"HTTP.METHOD:request.firstline.method\")\n              .expectValuePresent(\"HTTP.URI:request.firstline.uri\")\n              .expectValuePresent(\"HTTP.PROTOCOL:request.firstline.protocol\")\n              .expectValuePresent(\"HTTP.PROTOCOL.VERSION:request.firstline.protocol.version\")\n              .expectValuePresent(\"STRING:request.status.last\")\n              .expectValuePresent(\"BYTES:response.body.bytes\")\n              .expectValuePresent(\"MICROSECONDS:response.server.processing.time\")\n              .expectAbsentString(\"HTTP.QUERYSTRING:request.firstline.uri.query\")\n              .expectValuePresent(\"HTTP.PATH:request.firstline.uri.path\")\n              .expectAbsentString(\"HTTP.REF:request.firstline.uri.ref\");\n\n        for (String path: parser.getPossiblePaths()){\n            tester.expectPossible(path);\n        }\n\n        tester.checkExpectations();\n    }\n\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/test/java/nl/basjes/parse/httpdlog/MultiLineHttpdLogParserTest.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage nl.basjes.parse.httpdlog;\n\nimport nl.basjes.parse.core.Field;\nimport nl.basjes.parse.core.Parser;\nimport nl.basjes.parse.core.exceptions.DissectionFailure;\nimport nl.basjes.parse.core.exceptions.InvalidDissectorException;\nimport nl.basjes.parse.core.exceptions.MissingDissectorsException;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\nclass MultiLineHttpdLogParserTest {\n\n    // ------------------------------------------\n\n    public static class TestRecord {\n        private final Map<String, String> results = new HashMap<>(32);\n\n        @SuppressWarnings(\"UnusedDeclaration\")\n        @Field({\n            \"IP:connection.client.host\",\n            \"TIME.STAMP:request.receive.time\",\n            \"TIME.SECOND:request.receive.time.second\",\n            \"STRING:request.status.last\",\n            \"BYTESCLF:response.body.bytes\",\n            \"HTTP.URI:request.firstline.uri\",\n            \"HTTP.URI:request.referer\",\n            \"HTTP.USERAGENT:request.user-agent\"})\n        public void setValue(final String name, final String value) {\n            results.put(name, value);\n        }\n\n        public Map<String, String> getResults() {\n            return results;\n        }\n    }\n\n    // ------------------------------------------\n\n    /**\n     * Test of initialize method, of class ApacheHttpdLogParser.\n     */\n    @Test\n    void fullTest1() throws Exception {\n\n        String logFormat = LOG_FORMAT_1 + '\\n'\n                         + '\\n'\n                         + LOG_FORMAT_2 + '\\n'\n                         + '\\n';\n\n        Parser<TestRecord> parser = new HttpdLoglineParser<>(TestRecord.class, logFormat);\n\n        validateLine1(parser);\n        validateLine1(parser);\n        validateLine2(parser);\n        validateLine2(parser);\n        validateLine1(parser);\n        validateLine1(parser);\n        validateLine2(parser);\n        validateLine2(parser);\n        validateLine1(parser);\n        validateLine1(parser);\n        validateLine2(parser);\n        validateLine2(parser);\n    }\n\n    private static final String LOG_FORMAT_1 = \"%h %t \\\"%r\\\" %>s %b \\\"%{Referer}i\\\"\";\n    private static final String LINE_1 = \"127.0.0.1 [31/Dec/2012:23:49:41 +0100] \"\n            + \"\\\"GET /foo HTTP/1.1\\\" 200 \"\n            + \"1213 \\\"http://localhost/index.php?mies=wim\\\"\";\n\n    private void validateLine1(Parser<TestRecord> parser) throws InvalidDissectorException, MissingDissectorsException, DissectionFailure {\n        TestRecord record = new TestRecord();\n        parser.parse(record, LINE_1);\n        Map<String, String> results = record.getResults();\n\n        assertEquals(\"127.0.0.1\", results.get(\"IP:connection.client.host\"));\n        assertEquals(\"31/Dec/2012:23:49:41 +0100\", results.get(\"TIME.STAMP:request.receive.time\"));\n        assertEquals(\"/foo\", results.get(\"HTTP.URI:request.firstline.uri\"));\n        assertEquals(\"200\", results.get(\"STRING:request.status.last\"));\n        assertEquals(\"1213\", results.get(\"BYTESCLF:response.body.bytes\"));\n        assertEquals(\"http://localhost/index.php?mies=wim\", results.get(\"HTTP.URI:request.referer\"));\n        assertEquals(null, results.get(\"HTTP.USERAGENT:request.user-agent\"));\n    }\n\n    private static final String LOG_FORMAT_2 = \"%h %t \\\"%r\\\" %>s \\\"%{User-Agent}i\\\"\";\n    private static final String LINE_2 = \"127.0.0.2 [31/Dec/2012:23:49:42 +0100] \"\n            + \"\\\"GET /foo HTTP/1.1\\\" 404 \"\n            + \"\\\"Mozilla/5.0 (X11; Linux i686 on x86_64; rv:11.0) Gecko/20100101 Firefox/11.0\\\"\";\n\n    private void validateLine2(Parser<TestRecord> parser) throws InvalidDissectorException, MissingDissectorsException, DissectionFailure {\n        TestRecord record = new TestRecord();\n        parser.parse(record, LINE_2);\n        Map<String, String> results = record.getResults();\n\n        assertEquals(\"127.0.0.2\", results.get(\"IP:connection.client.host\"));\n        assertEquals(\"31/Dec/2012:23:49:42 +0100\", results.get(\"TIME.STAMP:request.receive.time\"));\n        assertEquals(\"/foo\", results.get(\"HTTP.URI:request.firstline.uri\"));\n        assertEquals(\"404\", results.get(\"STRING:request.status.last\"));\n        assertEquals(null, results.get(\"BYTESCLF:response.body.bytes\"));\n        assertEquals(null, results.get(\"HTTP.URI:request.referer\"));\n        assertEquals(\"Mozilla/5.0 (X11; Linux i686 on x86_64; rv:11.0) Gecko/20100101 Firefox/11.0\",\n                results.get(\"HTTP.USERAGENT:request.user-agent\"));\n    }\n\n    // ------------------------------------------\n\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/test/java/nl/basjes/parse/httpdlog/NginxLogFormatJsonTest.java",
    "content": "/*\n * Apache HTTPD logparsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage nl.basjes.parse.httpdlog;\n\nimport nl.basjes.parse.core.test.DissectorTester;\nimport nl.basjes.parse.core.test.TestRecord;\nimport org.junit.jupiter.api.Test;\n\nclass NginxLogFormatJsonTest {\n\n    @Test\n    void testJsonFormat() {\n\n        String logFormat =  \"{ \" +\n            \"\\\"message\\\":\\\"$request_uri\\\",\" +\n            \"\\\"client\\\": \\\"$remote_addr\\\",\" +\n            \"\\\"auth\\\": \\\"$remote_user\\\", \" +\n            \"\\\"bytes\\\": \\\"$body_bytes_sent\\\", \" +\n            \"\\\"time_in_sec\\\": \\\"$request_time\\\", \" +\n            \"\\\"response\\\": \\\"$status\\\", \" +\n            \"\\\"verb\\\":\\\"$request_method\\\",\" +\n            \"\\\"referrer\\\": \\\"$http_referer\\\", \" +\n            \"\\\"site\\\":\\\"$http_host\\\",\" +\n            \"\\\"httpversion\\\":\\\"$server_protocol\\\",\" +\n            \"\\\"logtype\\\":\\\"accesslog\\\",\" +\n            \"\\\"agent\\\": \\\"$http_user_agent\\\" }\";\n\n        String logLine = \"{ \" +\n            \"\\\"message\\\":\\\"/one/two/tool.git/info/refs?service=upload-pack\\\",\" +\n            \"\\\"client\\\": \\\"10.11.12.13\\\",\" +\n            \"\\\"auth\\\": \\\"-\\\", \" +\n            \"\\\"bytes\\\": \\\"178\\\", \" +\n            \"\\\"time_in_sec\\\": \\\"0.000\\\", \" +\n            \"\\\"response\\\": \\\"301\\\", \" +\n            \"\\\"verb\\\":\\\"GET\\\",\" +\n            \"\\\"referrer\\\": \\\"-\\\", \" +\n            \"\\\"site\\\":\\\"some.thing.example.com\\\",\" +\n            \"\\\"httpversion\\\":\\\"HTTP/1.1\\\",\" +\n            \"\\\"logtype\\\":\\\"accesslog\\\",\" +\n            \"\\\"agent\\\": \\\"git/1.9.5.msysgit.0\\\" }\";\n\n        DissectorTester.create()\n            .verbose()\n            .withParser(new HttpdLoglineParser<>(TestRecord.class, logFormat))\n            .withInput(logLine)\n\n            .printPossible()\n            .printAllPossibleValues();\n    }\n\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/test/java/nl/basjes/parse/httpdlog/NginxLogFormatTest.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage nl.basjes.parse.httpdlog;\n\nimport nl.basjes.parse.core.Parser;\nimport nl.basjes.parse.core.exceptions.DissectionFailure;\nimport nl.basjes.parse.core.exceptions.InvalidDissectorException;\nimport nl.basjes.parse.core.exceptions.MissingDissectorsException;\nimport nl.basjes.parse.core.test.DissectorTester;\nimport nl.basjes.parse.core.test.TestRecord;\nimport org.junit.jupiter.api.Test;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\n// CHECKSTYLE.OFF: LineLength\npublic class NginxLogFormatTest {\n\n    private static final Logger LOG = LoggerFactory.getLogger(NginxLogFormatTest.class);\n\n    @Test\n    void testBasicLogFormat() {\n        // From: http://articles.slicehost.com/2010/8/27/customizing-nginx-web-logs\n        String logFormat = \"$remote_addr - $remote_user [$time_local] \\\"$request\\\" $status $body_bytes_sent \\\"$http_referer\\\" \\\"$http_user_agent\\\"\";\n        String logLine = \"123.65.150.10 - - [23/Aug/2010:03:50:59 +0000] \\\"POST /wordpress3/wp-admin/admin-ajax.php HTTP/1.1\\\" 200 2 \\\"http://www.example.com/wordpress3/wp-admin/post-new.php\\\" \\\"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_4; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.472.25 Safari/534.3\\\"\";\n\n        DissectorTester.create()\n            .verbose()\n            .withParser(new HttpdLoglineParser<>(TestRecord.class, logFormat))\n            .withInput(logLine)\n\n            .printPossible()\n            .printAllPossibleValues();\n    }\n\n    @Test\n    void testBasicLogFormatDissector() {\n        // From: http://articles.slicehost.com/2010/8/27/customizing-nginx-web-logs\n        String logFormat = \"combined\";\n        String logLine = \"123.65.150.10 - - [23/Aug/2010:03:50:59 +0000] \\\"POST /wordpress3/wp-admin/admin-ajax.php HTTP/1.1\\\" 200 2 \\\"http://www.example.com/wordpress3/wp-admin/post-new.php\\\" \\\"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_4; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.472.25 Safari/534.3\\\"\";\n\n        HttpdLoglineParser<TestRecord> parser = new HttpdLoglineParser<>(TestRecord.class, \"$msec\");\n        parser.dropDissector(HttpdLogFormatDissector.class);\n\n        NginxHttpdLogFormatDissector dissector = new NginxHttpdLogFormatDissector();\n        dissector.setLogFormat(logFormat);\n        parser.addDissector(dissector);\n\n        DissectorTester.create()\n            .verbose()\n            .withParser(parser)\n            .withInput(logLine)\n            .printPossible()\n            .printAllPossibleValues();\n    }\n\n    @Test\n    void testBasicLogFormatWithUnknownField() {\n        // $remote_user_age is fake and doesn't exist.\n        String logFormat = \"$foobar $remote_user_age $remote_addr - $remote_user [$time_local] \\\"$request\\\" $status $body_bytes_sent \\\"$http_referer\\\" \\\"$http_user_agent\\\"\";\n        String logLine = \"something 42 123.65.150.10 - - [23/Aug/2010:03:50:59 +0000] \\\"POST /wordpress3/wp-admin/admin-ajax.php HTTP/1.1\\\" 200 2 \\\"http://www.example.com/wordpress3/wp-admin/post-new.php\\\" \\\"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_4; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.472.25 Safari/534.3\\\"\";\n\n        DissectorTester.create()\n            .verbose()\n            .withParser(new HttpdLoglineParser<>(TestRecord.class, logFormat))\n            .withInput(logLine)\n            .expect(\"UNKNOWN_NGINX_VARIABLE:nginx.unknown.foobar\", \"something\")\n            .expect(\"UNKNOWN_NGINX_VARIABLE:nginx.unknown.remote_user_age\", \"42\")\n            .checkExpectations()\n            .printPossible()\n            .printAllPossibleValues();\n    }\n\n\n    @Test\n    void testCompareApacheAndNginxOutput() throws NoSuchMethodException, InvalidDissectorException, MissingDissectorsException, DissectionFailure {\n\n        String logFormatNginx  = \"$remote_addr - $remote_user [$time_local] \\\"$request\\\" $status $body_bytes_sent \\\"$http_referer\\\" \\\"$http_user_agent\\\"\";\n\n        // combined format except the logname was removed.\n        String logFormatApache = \"%h - %u %t \\\"%r\\\" %>s %b \\\"%{Referer}i\\\" \\\"%{User-Agent}i\\\"\";\n\n        String logLine = \"1.2.3.4 - - [23/Aug/2010:03:50:59 +0000] \\\"POST /foo.html?aap&noot=mies HTTP/1.1\\\" 200 2 \\\"http://www.example.com/bar.html?wim&zus=jet\\\" \\\"Niels Basjes/1.0\\\"\";\n\n        Parser<TestRecord> apacheParser = new HttpdLoglineParser<>(TestRecord.class, logFormatApache);\n\n        String[] fieldsArray = {\n            \"HTTP.URI:request.referer\",\n            \"HTTP.PROTOCOL:request.referer.protocol\",\n            \"HTTP.USERINFO:request.referer.userinfo\",\n            \"HTTP.HOST:request.referer.host\",\n            \"HTTP.PORT:request.referer.port\",\n            \"HTTP.PATH:request.referer.path\",\n            \"HTTP.QUERYSTRING:request.referer.query\",\n            \"STRING:request.referer.query.*\",\n            \"HTTP.REF:request.referer.ref\",\n            \"TIME.STAMP:request.receive.time\",\n            \"TIME.DAY:request.receive.time.day\",\n            \"TIME.MONTHNAME:request.receive.time.monthname\",\n            \"TIME.MONTH:request.receive.time.month\",\n            \"TIME.WEEK:request.receive.time.weekofweekyear\",\n            \"TIME.YEAR:request.receive.time.weekyear\",\n            \"TIME.YEAR:request.receive.time.year\",\n            \"TIME.HOUR:request.receive.time.hour\",\n            \"TIME.MINUTE:request.receive.time.minute\",\n            \"TIME.SECOND:request.receive.time.second\",\n            \"TIME.MILLISECOND:request.receive.time.millisecond\",\n            \"TIME.DATE:request.receive.time.date\",\n            \"TIME.TIME:request.receive.time.time\",\n            \"TIME.ZONE:request.receive.time.timezone\",\n            \"TIME.EPOCH:request.receive.time.epoch\",\n            \"TIME.DAY:request.receive.time.day_utc\",\n            \"TIME.MONTHNAME:request.receive.time.monthname_utc\",\n            \"TIME.MONTH:request.receive.time.month_utc\",\n            \"TIME.WEEK:request.receive.time.weekofweekyear_utc\",\n            \"TIME.YEAR:request.receive.time.weekyear_utc\",\n            \"TIME.YEAR:request.receive.time.year_utc\",\n            \"TIME.HOUR:request.receive.time.hour_utc\",\n            \"TIME.MINUTE:request.receive.time.minute_utc\",\n            \"TIME.SECOND:request.receive.time.second_utc\",\n            \"TIME.MILLISECOND:request.receive.time.millisecond_utc\",\n            \"TIME.DATE:request.receive.time.date_utc\",\n            \"TIME.TIME:request.receive.time.time_utc\",\n            \"BYTESCLF:response.body.bytes\",\n            \"BYTES:response.body.bytes\",\n            \"STRING:request.status.last\",\n            \"HTTP.USERAGENT:request.user-agent\",\n            \"HTTP.FIRSTLINE:request.firstline\",\n            \"HTTP.METHOD:request.firstline.method\",\n            \"HTTP.URI:request.firstline.uri\",\n            \"HTTP.PROTOCOL:request.firstline.uri.protocol\",\n            \"HTTP.USERINFO:request.firstline.uri.userinfo\",\n            \"HTTP.HOST:request.firstline.uri.host\",\n            \"HTTP.PORT:request.firstline.uri.port\",\n            \"HTTP.PATH:request.firstline.uri.path\",\n            \"HTTP.QUERYSTRING:request.firstline.uri.query\",\n            \"STRING:request.firstline.uri.query.*\",\n            \"HTTP.REF:request.firstline.uri.ref\",\n            \"HTTP.PROTOCOL_VERSION:request.firstline.protocol\",\n            \"HTTP.PROTOCOL:request.firstline.protocol\",\n            \"HTTP.PROTOCOL.VERSION:request.firstline.protocol.version\",\n            \"IP:connection.client.host\"\n        };\n\n        List<String> fields = Arrays.asList(fieldsArray); // apacheParser.getPossiblePaths();\n\n        ArrayList<String> checkFields = new ArrayList<>(fields.size() + 10);\n        String[] parameterNames = {\"aap\", \"noot\", \"mies\", \"wim\", \"zus\", \"jet\" };\n        for (String field: fields) {\n            if (field.endsWith(\".*\")){\n                String fieldHead = field.substring(0, field.length()-1);\n                for (String parameterName: parameterNames){\n                    checkFields.add(fieldHead + parameterName);\n                }\n            } else {\n                checkFields.add(field);\n            }\n        }\n\n        Parser<TestRecord> nginxParser = new HttpdLoglineParser<>(TestRecord.class, logFormatNginx);\n        for (String field: checkFields) {\n            apacheParser.addParseTarget(TestRecord.class.getMethod(\"setStringValue\", String.class, String.class), field);\n            nginxParser.addParseTarget(TestRecord.class.getMethod(\"setStringValue\", String.class, String.class), field);\n        }\n\n        LOG.info(\"Running Apache parser\");\n        TestRecord apacheRecord = apacheParser.parse(logLine);\n        LOG.info(\"Running Nginx parser\");\n        TestRecord nginxRecord  = nginxParser.parse(logLine);\n\n        for (String field: checkFields) {\n            boolean apacheHasValue = apacheRecord.hasStringValue(field);\n            boolean nginxHasValue = nginxRecord.hasStringValue(field);\n            assertEquals(apacheHasValue, nginxHasValue, \"Apache and Nginx values for field \" + field + \" are different.\");\n\n            if (apacheRecord.hasStringValue(field)) {\n                String apacheValue = apacheRecord.getStringValue(field);\n                String nginxValue = nginxRecord.getStringValue(field);\n                assertEquals(apacheValue, nginxValue, \"Apache and Nginx values for field \" + field + \" are different.\");\n            }\n\n        }\n\n    }\n\n\n    @Test\n    void testFullTestAllFields() {\n        String logFormat =\n                \"# \\\"$status\\\" \" +\n                \"# \\\"$time_iso8601\\\" \" +\n                \"# \\\"$time_local\\\" \" +\n                \"# \\\"$arg_name\\\" \" +\n                \"# \\\"$args\\\" \" +\n                \"# \\\"$query_string\\\" \" +\n                \"# \\\"$binary_remote_addr\\\" \" +\n                \"# \\\"$body_bytes_sent\\\" \" +\n                \"# \\\"$bytes_sent\\\" \" +\n                \"# \\\"$connection\\\" \" +\n                \"# \\\"$connection_requests\\\" \" +\n                \"# \\\"$content_length\\\" \" +\n                \"# \\\"$content_type\\\" \" +\n                \"# \\\"$cookie_name\\\" \" +\n                \"# \\\"$document_root\\\" \" +\n                \"# \\\"$host\\\" \" +\n                \"# \\\"$hostname\\\" \" +\n                \"# \\\"$http_name\\\" \" +\n                \"# \\\"$https\\\" \" +\n                \"# \\\"$is_args\\\" \" +\n                \"# \\\"$limit_rate\\\" \" +\n                \"# \\\"$msec\\\" \" +\n                \"# \\\"$nginx_version\\\" \" +\n                \"# \\\"$pid\\\" \" +\n                \"# \\\"$pipe\\\" \" +\n                \"# \\\"$proxy_protocol_addr\\\" \" +\n                \"# \\\"$realpath_root\\\" \" +\n                \"# \\\"$remote_addr\\\" \" +\n                \"# \\\"$remote_port\\\" \" +\n                \"# \\\"$remote_user\\\" \" +\n                \"# \\\"$request\\\" \" +\n                \"# \\\"$request_body\\\" \" +\n                \"# \\\"$request_body_file\\\" \" +\n                \"# \\\"$request_completion\\\" \" +\n                \"# \\\"$request_filename\\\" \" +\n                \"# \\\"$request_length\\\" \" +\n                \"# \\\"$request_method\\\" \" +\n                \"# \\\"$request_time\\\" \" +\n                \"# \\\"$request_uri\\\" \" +\n                \"# \\\"$scheme\\\" \" +\n                \"# \\\"$sent_http_etag\\\" \" +\n                \"# \\\"$sent_http_last_modified\\\" \" +\n                \"# \\\"$server_addr\\\" \" +\n                \"# \\\"$server_name\\\" \" +\n                \"# \\\"$server_port\\\" \" +\n                \"# \\\"$server_protocol\\\" \" +\n                \"# \\\"$tcpinfo_rtt\\\" \" +\n                \"# \\\"$tcpinfo_rttvar\\\" \" +\n                \"# \\\"$tcpinfo_snd_cwnd\\\" \" +\n                \"# \\\"$tcpinfo_rcv_space\\\" \" +\n                \"# \\\"$uri\\\" \" +\n                \"# \\\"$document_uri\\\" \" +\n                \"# \\\"$http_user_agent\\\" \" +\n                \"# \\\"$http_referer\\\" \" +\n                \"#\";\n        String logLine =\n              /* $status                  */  \"# \\\"200\\\" \" +\n              /* $time_iso8601            */  \"# \\\"2017-01-03T15:56:36+01:00\\\" \" +\n              /* $time_local              */  \"# \\\"03/Jan/2017:15:56:36 +0100\\\" \" +\n              /* $arg_name                */  \"# \\\"-\\\" \" +\n              /* $args                    */  \"# \\\"aap&noot=&mies=wim\\\" \" +\n              /* $query_string            */  \"# \\\"aap&noot=&mies=wim\\\" \" +\n              /* $binary_remote_addr      */  \"# \\\"\\\\x7F\\\\x00\\\\x00\\\\x01\\\" \" +\n              /* $body_bytes_sent         */  \"# \\\"436\\\" \" +\n              /* $bytes_sent              */  \"# \\\"694\\\" \" +\n              /* $connection              */  \"# \\\"5\\\" \" +\n              /* $connection_requests     */  \"# \\\"4\\\" \" +\n              /* $content_length          */  \"# \\\"-\\\" \" +\n              /* $content_type            */  \"# \\\"-\\\" \" +\n              /* $cookie_name             */  \"# \\\"-\\\" \" +\n              /* $document_root           */  \"# \\\"/var/www/html\\\" \" +\n              /* $host                    */  \"# \\\"localhost\\\" \" +\n              /* $hostname                */  \"# \\\"hackbox\\\" \" +\n              /* $http_name               */  \"# \\\"-\\\" \" +\n              /* $https                   */  \"# \\\"\\\" \" +\n              /* $is_args                 */  \"# \\\"?\\\" \" +\n              /* $limit_rate              */  \"# \\\"0\\\" \" +\n              /* $msec                    */  \"# \\\"1483455396.639\\\" \" +\n              /* $nginx_version           */  \"# \\\"1.10.0\\\" \" +\n              /* $pid                     */  \"# \\\"5137\\\" \" +\n              /* $pipe                    */  \"# \\\".\\\" \" +\n              /* $proxy_protocol_addr     */  \"# \\\"\\\" \" +\n              /* $realpath_root           */  \"# \\\"/var/www/html\\\" \" +\n              /* $remote_addr             */  \"# \\\"127.0.0.1\\\" \" +\n              /* $remote_port             */  \"# \\\"44448\\\" \" +\n              /* $remote_user             */  \"# \\\"-\\\" \" +\n              /* $request                 */  \"# \\\"GET /?aap&noot=&mies=wim HTTP/1.1\\\" \" +\n              /* $request_body            */  \"# \\\"-\\\" \" +\n              /* $request_body_file       */  \"# \\\"-\\\" \" +\n              /* $request_completion      */  \"# \\\"OK\\\" \" +\n              /* $request_filename        */  \"# \\\"/var/www/html/index.html\\\" \" +\n              /* $request_length          */  \"# \\\"491\\\" \" +\n              /* $request_method          */  \"# \\\"GET\\\" \" +\n              /* $request_time            */  \"# \\\"0.000\\\" \" +\n              /* $request_uri             */  \"# \\\"/?aap&noot=&mies=wim\\\" \" +\n              /* $scheme                  */  \"# \\\"http\\\" \" +\n              /* $sent_http_etag          */  \"# \\\"W/\\\\x22586bbb8b-29e\\\\x22\\\" \" +\n              /* $sent_http_last_modified */  \"# \\\"Tue, 03 Jan 2017 14:56:11 GMT\\\" \" +\n              /* $server_addr             */  \"# \\\"127.0.0.1\\\" \" +\n              /* $server_name             */  \"# \\\"_\\\" \" +\n              /* $server_port             */  \"# \\\"80\\\" \" +\n              /* $server_protocol         */  \"# \\\"HTTP/1.1\\\" \" +\n              /* $tcpinfo_rtt             */  \"# \\\"52\\\" \" +\n              /* $tcpinfo_rttvar          */  \"# \\\"30\\\" \" +\n              /* $tcpinfo_snd_cwnd        */  \"# \\\"10\\\" \" +\n              /* $tcpinfo_rcv_space       */  \"# \\\"43690\\\" \" +\n              /* $uri                     */  \"# \\\"/index.html\\\" \" +\n              /* $document_uri            */  \"# \\\"/index.html\\\" \" +\n              /* $http_user_agent         */  \"# \\\"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.100 Safari/537.36\\\" \" +\n              /* $http_referer            */  \"# \\\"http://localhost/\\\" \" +\n                                              \"#\";\n\n        DissectorTester.create()\n            .verbose()\n            .withParser(new HttpdLoglineParser<>(TestRecord.class, logFormat))\n            .withInput(logLine)\n\n//            .printPossible()\n            .printAllPossibleValues();\n    }\n\n    private static class SingleFieldTestcase {\n        final String logformat;\n        final String logline;\n        final String fieldName;\n        final String expectedValue;\n\n        SingleFieldTestcase(String logformat, String logline, String fieldName, String expectedValue) {\n            this.logformat = logformat;\n            this.logline = logline;\n            this.fieldName = fieldName;\n            this.expectedValue = expectedValue;\n        }\n    }\n\n    @Test\n    void validateAllFields() {\n        List<SingleFieldTestcase> fieldsTests = new ArrayList<>();\n\n        fieldsTests.add(new SingleFieldTestcase(\"$status\",                   \"200\",                                 \"STRING:request.status.last\",                                   \"200\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$time_iso8601\",             \"2017-01-03T15:56:36+01:00\",           \"TIME.ISO8601:request.receive.time\",                            \"2017-01-03T15:56:36+01:00\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$time_local\",               \"03/Jan/2017:15:56:36 +0100\",          \"TIME.STAMP:request.receive.time\",                              \"03/Jan/2017:15:56:36 +0100\"));\n\n        fieldsTests.add(new SingleFieldTestcase(\"$time_iso8601\",             \"2017-01-03T15:56:36+01:00\",           \"TIME.EPOCH:request.receive.time.epoch\",                        \"1483455396000\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$time_local\",               \"03/Jan/2017:15:56:36 +0100\",          \"TIME.EPOCH:request.receive.time.epoch\",                        \"1483455396000\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$msec\",                     \"1483455396.639\",                      \"TIME.EPOCH:request.receive.time.epoch\",                        \"1483455396639\"));\n\n        fieldsTests.add(new SingleFieldTestcase(\"$remote_addr\",              \"127.0.0.1\",                           \"IP:connection.client.host\",                                    \"127.0.0.1\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$binary_remote_addr\",       \"\\\\x7F\\\\x00\\\\x00\\\\x01\",                \"IP_BINARY:connection.client.host\",                             \"\\\\x7F\\\\x00\\\\x00\\\\x01\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$binary_remote_addr\",       \"\\\\x7F\\\\x00\\\\x00\\\\x01\",                \"IP:connection.client.host\",                                    \"127.0.0.1\"));\n\n        fieldsTests.add(new SingleFieldTestcase(\"$remote_port\",              \"44448\",                               \"PORT:connection.client.port\",                                  \"44448\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$remote_user\",              \"-\",                                   \"STRING:connection.client.user\",                                null));\n\n        fieldsTests.add(new SingleFieldTestcase(\"$is_args\",                  \"?\",                                   \"STRING:request.firstline.uri.is_args\",                         \"?\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$query_string\",             \"aap&noot=&mies=wim\",                  \"HTTP.QUERYSTRING:request.firstline.uri.query\",                 \"aap&noot=&mies=wim\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$args\",                     \"aap&noot=&mies=wim\",                  \"HTTP.QUERYSTRING:request.firstline.uri.query\",                 \"aap&noot=&mies=wim\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$args\",                     \"aap&noot=&mies=wim\",                  \"STRING:request.firstline.uri.query.aap\",                       \"\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$args\",                     \"aap&noot=&mies=wim\",                  \"STRING:request.firstline.uri.query.noot\",                      \"\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$args\",                     \"aap&noot=&mies=wim\",                  \"STRING:request.firstline.uri.query.mies\",                      \"wim\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$arg_name\",                 \"foo\",                                 \"STRING:request.firstline.uri.query.name\",                      \"foo\"));\n\n        fieldsTests.add(new SingleFieldTestcase(\"$bytes_sent\",               \"694\",                                 \"BYTES:response.bytes\",                                         \"694\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$bytes_received\",           \"694\",                                 \"BYTES:request.bytes\",                                          \"694\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$body_bytes_sent\",          \"436\",                                 \"BYTES:response.body.bytes\",                                    \"436\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$connection\",               \"5\",                                   \"NUMBER:connection.serial_number\",                              \"5\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$connection_requests\",      \"4\",                                   \"NUMBER:connection.requestnr\",                                  \"4\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$https\",                    \"\",                                    \"STRING:connection.https\",                                      \"\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$content_length\",           \"-\",                                   \"HTTP.HEADER:request.header.content_length\",                    null));\n        fieldsTests.add(new SingleFieldTestcase(\"$content_type\",             \"-\",                                   \"HTTP.HEADER:request.header.content_type\",                      null));\n        fieldsTests.add(new SingleFieldTestcase(\"$cookie_name\",              \"Something\",                           \"HTTP.COOKIE:request.cookies.name\",                             \"Something\"));\n\n        fieldsTests.add(new SingleFieldTestcase(\"$document_root\",            \"/var/www/html\",                       \"STRING:request.firstline.document_root\",                       \"/var/www/html\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$realpath_root\",            \"/var/www/html\",                       \"STRING:request.firstline.realpath_root\",                       \"/var/www/html\"));\n\n        fieldsTests.add(new SingleFieldTestcase(\"$host\",                     \"localhost\",                           \"STRING:connection.server.name\",                                \"localhost\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$hostname\",                 \"hackbox\",                             \"STRING:connection.client.host\",                                \"hackbox\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$http_foobar\",              \"Something\",                           \"HTTP.HEADER:request.header.foobar\",                            \"Something\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$sent_http_foobar\",         \"Something\",                           \"HTTP.HEADER:response.header.foobar\",                           \"Something\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$sent_trailer_foobar\",      \"Something\",                           \"HTTP.TRAILER:response.trailer.foobar\",                         \"Something\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$nginx_version\",            \"1.10.0\",                              \"STRING:server.nginx.version\",                                  \"1.10.0\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$pid\",                      \"5137\",                                \"NUMBER:connection.server.child.processid\",                     \"5137\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$pipe\",                     \".\",                                   \"STRING:connection.nginx.pipe\",                                 \".\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$pipe\",                     \"p\",                                   \"STRING:connection.nginx.pipe\",                                 \"p\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$protocol\",                 \"TCP\",                                 \"STRING:connection.protocol\",                                   \"TCP\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$proxy_protocol_addr\",      \"1.2.3.4\",                             \"IP:connection.client.proxy.host\",                              \"1.2.3.4\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$proxy_protocol_port\",      \"1234\",                                \"PORT:connection.client.proxy.port\",                            \"1234\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$request\",                  \"GET /?aap&noot=&mies=wim HTTP/1.1\",   \"HTTP.FIRSTLINE:request.firstline\",                             \"GET /?aap&noot=&mies=wim HTTP/1.1\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$request_completion\",       \"OK\",                                  \"STRING:request.completion\",                                    \"OK\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$request_filename\",         \"/var/www/html/index.html\",            \"FILENAME:server.filename\",                                     \"/var/www/html/index.html\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$request_length\",           \"491\",                                 \"BYTES:request.bytes\",                                          \"491\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$request_method\",           \"GET\",                                 \"HTTP.METHOD:request.firstline.method\",                         \"GET\"));\n\n        fieldsTests.add(new SingleFieldTestcase(\"$request_time\",             \"123.456\",                             \"SECOND_MILLIS:response.server.processing.time\",                \"123.456\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$request_time\",             \"123.456\",                             \"MILLISECONDS:response.server.processing.time\",                 \"123456\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$request_time\",             \"123.456\",                             \"MICROSECONDS:response.server.processing.time\",                 \"123456000\"));\n\n        fieldsTests.add(new SingleFieldTestcase(\"$request_uri\",              \"/?aap&noot=&mies=wim\",                \"HTTP.URI:request.firstline.uri\",                               \"/?aap&noot=&mies=wim\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$scheme\",                   \"http\",                                \"HTTP.PROTOCOL:request.firstline.uri.protocol\",                 \"http\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$sent_http_etag\",           \"W/\\\\x22586bbb8b-29e\\\\x22\",            \"HTTP.HEADER:response.header.etag\",                             \"W/\\\\x22586bbb8b-29e\\\\x22\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$sent_http_last_modified\",  \"Tue, 03 Jan 2017 14:56:11 GMT\",       \"HTTP.HEADER:response.header.last_modified\",                    \"Tue, 03 Jan 2017 14:56:11 GMT\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$server_addr\",              \"127.0.0.1\",                           \"IP:connection.server.ip\",                                      \"127.0.0.1\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$server_name\",              \"_\",                                   \"STRING:connection.server.name\",                                \"_\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$server_port\",              \"80\",                                  \"PORT:connection.server.port\",                                  \"80\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$server_protocol\",          \"HTTP/1.1\",                            \"HTTP.PROTOCOL_VERSION:request.firstline.protocol\",             \"HTTP/1.1\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$server_protocol\",          \"HTTP/1.1\",                            \"HTTP.PROTOCOL:request.firstline.protocol\",                     \"HTTP\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$server_protocol\",          \"HTTP/1.1\",                            \"HTTP.PROTOCOL.VERSION:request.firstline.protocol.version\",     \"1.1\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$tcpinfo_rtt\",              \"52\",                                  \"MICROSECONDS:connection.tcpinfo.rtt\",                          \"52\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$tcpinfo_rttvar\",           \"30\",                                  \"MICROSECONDS:connection.tcpinfo.rttvar\",                       \"30\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$tcpinfo_snd_cwnd\",         \"10\",                                  \"BYTES:connection.tcpinfo.send.cwnd\",                           \"10\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$tcpinfo_rcv_space\",        \"43690\",                               \"BYTES:connection.tcpinfo.receive.space\",                       \"43690\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$uri\",                      \"/index.html\",                         \"HTTP.URI:request.firstline.uri.normalized\",                    \"/index.html\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$document_uri\",             \"/index.html\",                         \"HTTP.URI:request.firstline.uri.normalized\",                    \"/index.html\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$http_user_agent\",          \"Mozilla/5.0 (Foo)\",                   \"HTTP.USERAGENT:request.user-agent\",                            \"Mozilla/5.0 (Foo)\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$http_foo_user_agent\",      \"Mozilla/5.0 (Foo)\",                   \"HTTP.HEADER:request.header.foo_user_agent\",                    \"Mozilla/5.0 (Foo)\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$http_user_agent_foo\",      \"Mozilla/5.0 (Foo)\",                   \"HTTP.HEADER:request.header.user_agent_foo\",                    \"Mozilla/5.0 (Foo)\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$http_referer\",             \"http://localhost/\",                   \"HTTP.URI:request.referer\",                                     \"http://localhost/\"));\n\n        // TODO: Check if these are REALLY \"not intended for logging\"\n        fieldsTests.add(new SingleFieldTestcase(\"$request_body\",             \"-\",                                   \"NOT_IMPLEMENTED:nginx_parameter_not_intended_for_logging__request_body\",         null));\n        fieldsTests.add(new SingleFieldTestcase(\"$request_body_file\",        \"-\",                                   \"NOT_IMPLEMENTED:nginx_parameter_not_intended_for_logging__request_body_file\",    null));\n        fieldsTests.add(new SingleFieldTestcase(\"$limit_rate\",               \"0\",                                   \"NOT_IMPLEMENTED:nginx_parameter_not_intended_for_logging__limit_rate\",             \"0\"));\n\n        for (SingleFieldTestcase testCase: fieldsTests) {\n            DissectorTester.create()\n                .printSeparator()\n                .verbose()\n                .withParser(new HttpdLoglineParser<>(TestRecord.class, testCase.logformat))\n                .withInput(testCase.logline)\n                .expect(testCase.fieldName, testCase.expectedValue)\n                .printPossible()\n                .printAllPossibleValues()\n                .checkExpectations();\n        }\n\n    }\n\n    @Test\n    void validateAllFieldsPrefix() {\n        List<SingleFieldTestcase> fieldsTests = new ArrayList<>();\n\n        final String useragent = \"Mozilla/5.0 (Foo)\";\n        fieldsTests.add(new SingleFieldTestcase(\"$http_user_agent\",       useragent, \"HTTP.USERAGENT:request.user-agent\",         useragent));\n        fieldsTests.add(new SingleFieldTestcase(\"$http_foo_user_agent\",   useragent, \"HTTP.HEADER:request.header.foo_user_agent\", useragent));\n        fieldsTests.add(new SingleFieldTestcase(\"$http_user_agent_foo\",   useragent, \"HTTP.HEADER:request.header.user_agent_foo\", useragent));\n\n        for (SingleFieldTestcase testCase: fieldsTests) {\n            DissectorTester.create()\n                .printSeparator()\n                .verbose()\n                .withParser(new HttpdLoglineParser<>(TestRecord.class, testCase.logformat))\n                .withInput(testCase.logline)\n                .expect(testCase.fieldName, testCase.expectedValue)\n                .printPossible()\n                .printAllPossibleValues()\n                .checkExpectations();\n        }\n\n    }\n\n\n    @Test\n    void bugReport60(){\n\n        String logFormat = \"$the_real_ip - [$the_real_ip] - $remote_user [$time_local] \\\"$request\\\" $status $body_bytes_sent \\\"$http_referer\\\" \\\"$http_user_agent\\\" $request_length $request_time [$proxy_upstream_name] $upstream_addr $upstream_response_length $upstream_response_time $upstream_status $req_id\";\n\n        String logLine = \"1.2.3.4 - [1.2.3.4] - - [07/Aug/2020:15:50:07 +0800] \\\"HEAD /ai/search/version HTTP/1.1\\\" 308 0 \\\"-\\\" \\\"curl/7.29.0\\\" 100 0.000 [default-ai-search-prod-svc-5009] - - - - bfb9417db656d95bcfdf3e2a7f47b1ec\";\n\n        DissectorTester.create()\n            .verbose()\n            .withParser(new HttpdLoglineParser<>(TestRecord.class, logFormat))\n            .withInput(logLine)\n\n            .expect(\"STRING:connection.client.user\",                                  (String)null)\n            .expect(\"BYTES:request.bytes\",                                            \"100\")\n            .expect(\"BYTESCLF:request.bytes\",                                         \"100\")\n            .expect(\"STRING:nginxmodule.kubernetes.req_id\",                           \"bfb9417db656d95bcfdf3e2a7f47b1ec\")\n            .expect(\"HTTP.URI:request.referer\",                                       (String) null)\n            .expectAbsentString(\"HTTP.PROTOCOL:request.referer.protocol\")\n            .expectAbsentString(\"HTTP.USERINFO:request.referer.userinfo\")\n            .expectAbsentString(\"HTTP.HOST:request.referer.host\")\n            .expectAbsentString(\"HTTP.PORT:request.referer.port\")\n            .expectAbsentString(\"HTTP.PATH:request.referer.path\")\n            .expectAbsentString(\"HTTP.QUERYSTRING:request.referer.query\")\n            .expectAbsentString(\"STRING:request.referer.query.*\")\n            .expectAbsentString(\"HTTP.REF:request.referer.ref\")\n            .expect(\"SECOND_MILLIS:response.server.processing.time\",                  \"0.000\")\n            .expect(\"MILLISECONDS:response.server.processing.time\",                   \"0\")\n            .expect(\"MICROSECONDS:response.server.processing.time\",                   \"0\")\n            .expect(\"TIME.STAMP:request.receive.time\",                                \"07/Aug/2020:15:50:07 +0800\")\n            .expect(\"TIME.DAY:request.receive.time.day\",                              \"7\")\n            .expect(\"TIME.MONTHNAME:request.receive.time.monthname\",                  \"August\")\n            .expect(\"TIME.MONTH:request.receive.time.month\",                          \"8\")\n            .expect(\"TIME.WEEK:request.receive.time.weekofweekyear\",                  \"32\")\n            .expect(\"TIME.YEAR:request.receive.time.weekyear\",                        \"2020\")\n            .expect(\"TIME.YEAR:request.receive.time.year\",                            \"2020\")\n            .expect(\"TIME.HOUR:request.receive.time.hour\",                            \"15\")\n            .expect(\"TIME.MINUTE:request.receive.time.minute\",                        \"50\")\n            .expect(\"TIME.SECOND:request.receive.time.second\",                        \"7\")\n            .expect(\"TIME.MILLISECOND:request.receive.time.millisecond\",              \"0\")\n            .expect(\"TIME.MICROSECOND:request.receive.time.microsecond\",              \"0\")\n            .expect(\"TIME.NANOSECOND:request.receive.time.nanosecond\",                \"0\")\n            .expect(\"TIME.DATE:request.receive.time.date\",                            \"2020-08-07\")\n            .expect(\"TIME.TIME:request.receive.time.time\",                            \"15:50:07\")\n            .expect(\"TIME.ZONE:request.receive.time.timezone\",                        \"+08:00\")\n            .expect(\"TIME.EPOCH:request.receive.time.epoch\",                          \"1596786607000\")\n            .expect(\"TIME.DAY:request.receive.time.day_utc\",                          \"7\")\n            .expect(\"TIME.MONTHNAME:request.receive.time.monthname_utc\",              \"August\")\n            .expect(\"TIME.MONTH:request.receive.time.month_utc\",                      \"8\")\n            .expect(\"TIME.WEEK:request.receive.time.weekofweekyear_utc\",              \"32\")\n            .expect(\"TIME.YEAR:request.receive.time.weekyear_utc\",                    \"2020\")\n            .expect(\"TIME.YEAR:request.receive.time.year_utc\",                        \"2020\")\n            .expect(\"TIME.HOUR:request.receive.time.hour_utc\",                        \"7\")\n            .expect(\"TIME.MINUTE:request.receive.time.minute_utc\",                    \"50\")\n            .expect(\"TIME.SECOND:request.receive.time.second_utc\",                    \"7\")\n            .expect(\"TIME.MILLISECOND:request.receive.time.millisecond_utc\",          \"0\")\n            .expect(\"TIME.MICROSECOND:request.receive.time.microsecond_utc\",          \"0\")\n            .expect(\"TIME.NANOSECOND:request.receive.time.nanosecond_utc\",            \"0\")\n            .expect(\"TIME.DATE:request.receive.time.date_utc\",                        \"2020-08-07\")\n            .expect(\"TIME.TIME:request.receive.time.time_utc\",                        \"07:50:07\")\n            .expect(\"STRING:request.status.last\",                                     \"308\")\n            .expect(\"HTTP.USERAGENT:request.user-agent\",                              \"curl/7.29.0\")\n            .expect(\"IP:nginxmodule.kubernetes.the_real_ip\",                          \"1.2.3.4\")\n            .expect(\"HTTP.FIRSTLINE:request.firstline\",                               \"HEAD /ai/search/version HTTP/1.1\")\n            .expect(\"HTTP.METHOD:request.firstline.method\",                           \"HEAD\")\n            .expect(\"HTTP.URI:request.firstline.uri\",                                 \"/ai/search/version\")\n            .expectAbsentString(\"HTTP.PROTOCOL:request.firstline.uri.protocol\")\n            .expectAbsentString(\"HTTP.USERINFO:request.firstline.uri.userinfo\")\n            .expectAbsentString(\"HTTP.HOST:request.firstline.uri.host\")\n            .expectAbsentString(\"HTTP.PORT:request.firstline.uri.port\")\n            .expect(\"HTTP.PATH:request.firstline.uri.path\",                           \"/ai/search/version\")\n            .expectAbsentString(\"HTTP.QUERYSTRING:request.firstline.uri.query\")\n            .expectAbsentString(\"STRING:request.firstline.uri.query.*\")\n            .expectAbsentString(\"HTTP.REF:request.firstline.uri.ref\")\n            .expect(\"HTTP.PROTOCOL_VERSION:request.firstline.protocol\",               \"HTTP/1.1\")\n            .expect(\"HTTP.PROTOCOL:request.firstline.protocol\",                       \"HTTP\")\n            .expect(\"HTTP.PROTOCOL.VERSION:request.firstline.protocol.version\",       \"1.1\")\n            .expect(\"BYTES:response.body.bytes\",                                      \"0\")\n            .expect(\"BYTESCLF:response.body.bytes\",                                   0L)\n            .expect(\"STRING:nginxmodule.kubernetes.proxy_upstream_name\",              \"default-ai-search-prod-svc-5009\")\n\n            .expect(\"UPSTREAM_BYTES_LIST:nginxmodule.upstream.response.length\",       (String)null)\n            .expect(\"UPSTREAM_STATUS_LIST:nginxmodule.upstream.status\",               (String)null)\n            .expect(\"UPSTREAM_SECOND_MILLIS_LIST:nginxmodule.upstream.response.time\", (String)null)\n            .expect(\"UPSTREAM_ADDR_LIST:nginxmodule.upstream.addr\",                   (String)null)\n            .checkExpectations();\n    }\n\n    @Test\n    void bugReport227_bad(){\n        String logFormat = \"$remote_addr - $remote_user [$time_local] \\\"$request\\\" $status $body_bytes_sent \\\"$http_referer\\\" \\\"$http_user_agent\\\" \\\"-\\\"\";\n        String logLine = \"94.232.44.112 - - [28/Feb/2021:03:54:40 +0800] \\\"x00Cookie: mstshash=Administr\\\" 400 157 \\\"-\\\" \\\"-\\\" \\\"-\\\"\";\n        DissectorTester.create()\n//            .verbose()\n            .withParser(new HttpdLoglineParser<>(TestRecord.class, logFormat))\n            .withInput(logLine)\n            .expect(\"IP:connection.client.host\",                                            \"94.232.44.112\")\n            .expect(\"STRING:connection.client.user\",                                        (String)null)\n            .expect(\"HTTP.FIRSTLINE:request.firstline\",                                     \"x00Cookie: mstshash=Administr\")\n            .expectAbsentString(\"HTTP.METHOD:request.firstline.method\")\n            .expectAbsentString(\"HTTP.URI:request.firstline.uri\")\n            .expectAbsentString(\"HTTP.PROTOCOL:request.firstline.protocol\")\n            .expect(\"TIME.STAMP:request.receive.time\",                                      \"28/Feb/2021:03:54:40 +0800\")\n            .expect(\"TIME.DATE:request.receive.time.date\",                                  \"2021-02-28\")\n            .expect(\"TIME.TIME:request.receive.time.time\",                                  \"03:54:40\")\n            .expect(\"TIME.ZONE:request.receive.time.timezone\",                              \"+08:00\")\n            .expect(\"TIME.YEAR:request.receive.time.year\",                                  \"2021\")\n            .expect(\"TIME.MONTH:request.receive.time.month\",                                \"2\")\n            .expect(\"TIME.DAY:request.receive.time.day\",                                    \"28\")\n            .expect(\"TIME.HOUR:request.receive.time.hour\",                                  \"3\")\n            .expect(\"TIME.MINUTE:request.receive.time.minute\",                              \"54\")\n            .expect(\"TIME.SECOND:request.receive.time.second\",                              \"40\")\n            .expect(\"TIME.EPOCH:request.receive.time.epoch\",                                \"1614455680000\")\n            .expect(\"HTTP.URI:request.referer\",                                             (String)null)\n            .expect(\"STRING:request.status.last\",                                           \"400\")\n            .expect(\"BYTES:response.body.bytes\",                                            \"157\")\n            .expect(\"HTTP.USERAGENT:request.user-agent\",                                    (String)null)\n            .checkExpectations();\n    }\n\n    @Test\n    void bugReport227_empty() {\n        String logFormat = \"$remote_addr - $remote_user [$time_local] \\\"$request\\\" $status $body_bytes_sent \\\"$http_referer\\\" \\\"$http_user_agent\\\" \\\"-\\\"\";\n        String logLine = \"207.154.195.167 - - [01/Mar/2021:03:25:25 +0800] \\\"\\\" 400 0 \\\"-\\\" \\\"-\\\" \\\"-\\\"\";\n        DissectorTester.create()\n//            .verbose()\n            .withParser(new HttpdLoglineParser<>(TestRecord.class, logFormat))\n            .withInput(logLine)\n\n            .expect(\"IP:connection.client.host\",                                            \"207.154.195.167\")\n            .expect(\"STRING:connection.client.user\",                                        (String)null)\n            .expect(\"HTTP.FIRSTLINE:request.firstline\",                                     \"\")\n            .expectAbsentString(\"HTTP.METHOD:request.firstline.method\")\n            .expectAbsentString(\"HTTP.URI:request.firstline.uri\")\n            .expectAbsentString(\"HTTP.PROTOCOL:request.firstline.protocol\")\n            .expect(\"TIME.STAMP:request.receive.time\",                                      \"01/Mar/2021:03:25:25 +0800\")\n            .expect(\"TIME.DATE:request.receive.time.date\",                                  \"2021-03-01\")\n            .expect(\"TIME.TIME:request.receive.time.time\",                                  \"03:25:25\")\n            .expect(\"TIME.ZONE:request.receive.time.timezone\",                              \"+08:00\")\n            .expect(\"TIME.YEAR:request.receive.time.year\",                                  \"2021\")\n            .expect(\"TIME.MONTH:request.receive.time.month\",                                \"3\")\n            .expect(\"TIME.DAY:request.receive.time.day\",                                    \"1\")\n            .expect(\"TIME.HOUR:request.receive.time.hour\",                                  \"3\")\n            .expect(\"TIME.MINUTE:request.receive.time.minute\",                              \"25\")\n            .expect(\"TIME.SECOND:request.receive.time.second\",                              \"25\")\n            .expect(\"TIME.EPOCH:request.receive.time.epoch\",                                \"1614540325000\")\n            .expect(\"HTTP.URI:request.referer\",                                             (String)null)\n            .expect(\"STRING:request.status.last\",                                           \"400\")\n            .expect(\"BYTES:response.body.bytes\",                                            \"0\")\n            .expect(\"HTTP.USERAGENT:request.user-agent\",                                    (String)null)\n            .checkExpectations();\n    }\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/test/java/nl/basjes/parse/httpdlog/UtilsTest.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage nl.basjes.parse.httpdlog;\n\nimport org.junit.jupiter.api.Test;\n\nimport static nl.basjes.parse.httpdlog.Utils.makeHTMLEncodedInert;\nimport static nl.basjes.parse.httpdlog.Utils.resilientUrlDecode;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertNull;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\n\nclass UtilsTest {\n\n    @Test\n    void testUrlDecoder() {\n        // Normal cases\n        assertEquals(\"  \", resilientUrlDecode(\"  \"));\n        assertEquals(\"  \", resilientUrlDecode(\" %20\"));\n        assertEquals(\"  \", resilientUrlDecode(\"%20 \"));\n        assertEquals(\"  \", resilientUrlDecode(\"%20%20\"));\n        assertEquals(\"  \", resilientUrlDecode(\"%u0020%u0020\"));\n        assertEquals(\"  \", resilientUrlDecode(\"%20%u0020\"));\n        assertEquals(\"  \", resilientUrlDecode(\"%u0020%20\"));\n\n        // Deformed characters at the end of the line (desired is they are discarded)\n        assertEquals(\"x \", resilientUrlDecode(\"x %2\"));\n        assertEquals(\"x \", resilientUrlDecode(\"x%20%2\"));\n        assertEquals(\"x 2\", resilientUrlDecode(\"x%u202\"));\n        assertEquals(\"x \", resilientUrlDecode(\"x%u20\"));\n        assertEquals(\"x\", resilientUrlDecode(\"x%u2\"));\n        assertEquals(\"x\", resilientUrlDecode(\"x%u\"));\n        assertEquals(\"x\", resilientUrlDecode(\"x%\"));\n\n        // Combined test case (7 spaces and a chopped one)\n        assertEquals(\"       \", resilientUrlDecode(\"%20 %20%u0020%20 %20%2\"));\n    }\n\n    @Test\n    void testHtmlEncoding() {\n        assertEquals(\"<\", resilientUrlDecode(makeHTMLEncodedInert(\"&lt;\")));\n        assertEquals(\">\", resilientUrlDecode(makeHTMLEncodedInert(\"&gt;\")));\n        assertEquals(\"€\", resilientUrlDecode(makeHTMLEncodedInert(\"&euro;\")));\n\n        assertEquals(\"*#x12345;\", makeHTMLEncodedInert(\"&#x12345;\"));\n        assertEquals(\"*#xaBcDeF;\", makeHTMLEncodedInert(\"&#xaBcDeF;\"));\n    }\n\n    @Test\n    void testHtmlEncodingFull() {\n        // Normal cases\n        assertEquals(\"  \", resilientUrlDecode(makeHTMLEncodedInert(\"  \")));\n        assertEquals(\"  \", resilientUrlDecode(makeHTMLEncodedInert(\" %20\")));\n        assertEquals(\"  \", resilientUrlDecode(makeHTMLEncodedInert(\"%20 \")));\n        assertEquals(\"  \", resilientUrlDecode(makeHTMLEncodedInert(\"%20%20\")));\n        assertEquals(\"  \", resilientUrlDecode(makeHTMLEncodedInert(\"%u0020%u0020\")));\n        assertEquals(\"  \", resilientUrlDecode(makeHTMLEncodedInert(\"%20%u0020\")));\n        assertEquals(\"  \", resilientUrlDecode(makeHTMLEncodedInert(\"%u0020%20\")));\n\n        // Deformed characters at the end of the line (desired is they are discarded)\n        assertEquals(\"x \", resilientUrlDecode(makeHTMLEncodedInert(\"x %2\")));\n        assertEquals(\"x \", resilientUrlDecode(makeHTMLEncodedInert(\"x%20%2\")));\n        assertEquals(\"x 2\", resilientUrlDecode(makeHTMLEncodedInert(\"x%u202\")));\n        assertEquals(\"x \", resilientUrlDecode(makeHTMLEncodedInert(\"x%u20\")));\n        assertEquals(\"x\", resilientUrlDecode(makeHTMLEncodedInert(\"x%u2\")));\n        assertEquals(\"x\", resilientUrlDecode(makeHTMLEncodedInert(\"x%u\")));\n        assertEquals(\"x\", resilientUrlDecode(makeHTMLEncodedInert(\"x%\")));\n\n        // Combined test case (7 spaces and a chopped one)\n        assertEquals(\"       \", resilientUrlDecode(makeHTMLEncodedInert(\"%20 %20%u0020%20 %20%2\")));\n\n\n        // Normal cases\n        assertEquals(\" > \", resilientUrlDecode(makeHTMLEncodedInert(\" &gt; \")));\n        assertEquals(\" > \", resilientUrlDecode(makeHTMLEncodedInert(\" &gt;%20\")));\n        assertEquals(\" > \", resilientUrlDecode(makeHTMLEncodedInert(\"%20&gt; \")));\n        assertEquals(\" > \", resilientUrlDecode(makeHTMLEncodedInert(\"%20&gt;%20\")));\n        assertEquals(\" > \", resilientUrlDecode(makeHTMLEncodedInert(\"%u0020&gt;%u0020\")));\n        assertEquals(\" > \", resilientUrlDecode(makeHTMLEncodedInert(\"%20&gt;%u0020\")));\n        assertEquals(\" > \", resilientUrlDecode(makeHTMLEncodedInert(\"%u0020&gt;%20\")));\n\n        // Deformed characters at the end of the line (desired is they are discarded)\n        assertEquals(\">x \", resilientUrlDecode(makeHTMLEncodedInert(\"&gt;x %2\")));\n        assertEquals(\">x \", resilientUrlDecode(makeHTMLEncodedInert(\"&gt;x%20%2\")));\n        assertEquals(\">x 2\",  resilientUrlDecode(makeHTMLEncodedInert(\"&gt;x%u202\")));\n        assertEquals(\">x \",  resilientUrlDecode(makeHTMLEncodedInert(\"&gt;x%u20\")));\n        assertEquals(\">x\",  resilientUrlDecode(makeHTMLEncodedInert(\"&gt;x%u2\")));\n        assertEquals(\">x\",  resilientUrlDecode(makeHTMLEncodedInert(\"&gt;x%u\")));\n        assertEquals(\">x\",  resilientUrlDecode(makeHTMLEncodedInert(\"&gt;x%\")));\n\n\n        // Normal cases\n        assertEquals(\" *foobar; \", resilientUrlDecode(makeHTMLEncodedInert(\" &foobar; \")));\n        assertEquals(\" *foobar; \", resilientUrlDecode(makeHTMLEncodedInert(\" &foobar;%20\")));\n        assertEquals(\" *foobar; \", resilientUrlDecode(makeHTMLEncodedInert(\"%20&foobar; \")));\n        assertEquals(\" *foobar; \", resilientUrlDecode(makeHTMLEncodedInert(\"%20&foobar;%20\")));\n        assertEquals(\" *foobar; \", resilientUrlDecode(makeHTMLEncodedInert(\"%u0020&foobar;%u0020\")));\n        assertEquals(\" *foobar; \", resilientUrlDecode(makeHTMLEncodedInert(\"%20&foobar;%u0020\")));\n        assertEquals(\" *foobar; \", resilientUrlDecode(makeHTMLEncodedInert(\"%u0020&foobar;%20\")));\n\n        // Deformed characters at the end of the line (desired is they are discarded)\n        assertEquals(\"*foobar;x \", resilientUrlDecode(makeHTMLEncodedInert(\"&foobar;x %2\")));\n        assertEquals(\"*foobar;x \", resilientUrlDecode(makeHTMLEncodedInert(\"&foobar;x%20%2\")));\n        assertEquals(\"*foobar;x 2\",  resilientUrlDecode(makeHTMLEncodedInert(\"&foobar;x%u202\")));\n        assertEquals(\"*foobar;x \",  resilientUrlDecode(makeHTMLEncodedInert(\"&foobar;x%u20\")));\n        assertEquals(\"*foobar;x\",  resilientUrlDecode(makeHTMLEncodedInert(\"&foobar;x%u2\")));\n        assertEquals(\"*foobar;x\",  resilientUrlDecode(makeHTMLEncodedInert(\"&foobar;x%u\")));\n        assertEquals(\"*foobar;x\",  resilientUrlDecode(makeHTMLEncodedInert(\"&foobar;x%\")));\n\n        assertEquals(\"€\",  resilientUrlDecode(makeHTMLEncodedInert(\"&euro;\")));\n    }\n\n    @Test\n    void testHexToByte() {\n        // Test basic character decoder\n        assertEquals((byte) 0x00, Utils.hexCharsToByte('0', '0'));\n        assertEquals((byte) 0x11, Utils.hexCharsToByte('1', '1'));\n        assertEquals((byte) 0x22, Utils.hexCharsToByte('2', '2'));\n        assertEquals((byte) 0x33, Utils.hexCharsToByte('3', '3'));\n        assertEquals((byte) 0x44, Utils.hexCharsToByte('4', '4'));\n        assertEquals((byte) 0x55, Utils.hexCharsToByte('5', '5'));\n        assertEquals((byte) 0x66, Utils.hexCharsToByte('6', '6'));\n        assertEquals((byte) 0x77, Utils.hexCharsToByte('7', '7'));\n        assertEquals((byte) 0x88, Utils.hexCharsToByte('8', '8'));\n        assertEquals((byte) 0x99, Utils.hexCharsToByte('9', '9'));\n        assertEquals((byte) 0xaa, Utils.hexCharsToByte('a', 'a'));\n        assertEquals((byte) 0xbb, Utils.hexCharsToByte('b', 'b'));\n        assertEquals((byte) 0xcc, Utils.hexCharsToByte('c', 'c'));\n        assertEquals((byte) 0xdd, Utils.hexCharsToByte('d', 'd'));\n        assertEquals((byte) 0xee, Utils.hexCharsToByte('e', 'e'));\n        assertEquals((byte) 0xff, Utils.hexCharsToByte('f', 'f'));\n        assertEquals((byte) 0xAA, Utils.hexCharsToByte('A', 'A'));\n        assertEquals((byte) 0xBB, Utils.hexCharsToByte('B', 'B'));\n        assertEquals((byte) 0xCC, Utils.hexCharsToByte('C', 'C'));\n        assertEquals((byte) 0xDD, Utils.hexCharsToByte('D', 'D'));\n        assertEquals((byte) 0xEE, Utils.hexCharsToByte('E', 'E'));\n        assertEquals((byte) 0xFF, Utils.hexCharsToByte('F', 'F'));\n    }\n\n    @Test\n    void testHexToByteIllegalLeft() {\n        assertThrows(IllegalArgumentException.class, () -> {\n            Utils.hexCharsToByte('X', '0');\n        });\n    }\n\n    @Test\n    void testHexToByteIllegalRight() {\n        assertThrows(IllegalArgumentException.class, () -> {\n            Utils.hexCharsToByte('0', 'X');\n        });\n    }\n\n    @Test\n    void testApacheLogDecoder() {\n        // Decoding a value\n        assertEquals(\"bla bla bla\", Utils.decodeApacheHTTPDLogValue(\"bla bla bla\"));\n        assertEquals(\"bla bla bla\", Utils.decodeApacheHTTPDLogValue(\"bla\\\\x20bla bla\"));\n        assertEquals(\"bla\\bbla\\nbla\\tbla\", Utils.decodeApacheHTTPDLogValue(\"bla\\\\bbla\\\\nbla\\\\tbla\"));\n        assertEquals(\"bla\\\"bla\\nbla\\tbla\", Utils.decodeApacheHTTPDLogValue(\"bla\\\\\\\"bla\\\\nbla\\\\tbla\"));\n        assertEquals(new String(new byte[] {(byte)0x0b}), Utils.decodeApacheHTTPDLogValue(\"\\\\v\"));\n\n        // Specials\n        assertEquals(\"\\\\q\", Utils.decodeApacheHTTPDLogValue(\"\\\\q\"));\n        assertEquals(\"\", Utils.decodeApacheHTTPDLogValue(\"\"));\n        assertNull(Utils.decodeApacheHTTPDLogValue(null));\n    }\n\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/test/java/nl/basjes/parse/httpdlog/dissectors/TestCookieDissector.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage nl.basjes.parse.httpdlog.dissectors;\n\nimport nl.basjes.parse.core.test.DissectorTester;\nimport org.junit.jupiter.api.Test;\n\nclass TestCookieDissector {\n\n    @Test\n    void testRequestCookies() {\n        DissectorTester.create()\n            .withDissector(\"cookies\", new RequestCookieListDissector())\n\n            .withInput(\n                \"NBA-0; \" +\n                \"NBA-1=; \" +\n                \"NBA-2=1234; \")\n\n            .expect(\"HTTP.COOKIES:cookies\", \"NBA-0; NBA-1=; NBA-2=1234; \")\n            .expect(\"HTTP.COOKIE:cookies.nba-0\", \"\")\n            .expect(\"HTTP.COOKIE:cookies.nba-1\", \"\")\n            .expect(\"HTTP.COOKIE:cookies.nba-2\", \"1234\")\n\n            .checkExpectations();\n    }\n\n    @Test\n    void testResponseSetCookies() {\n\n        DissectorTester.create()\n            .withDissector(\"cookies\", new ResponseSetCookieListDissector())\n            .withDissector(new ResponseSetCookieDissector())\n\n            .withInput(\n                \"NBA-0=, \" +\n                \"NBA-1=1234, \" +\n                \"NBA-2=1234; expires=Wed, 01-Jan-2020 00:00:10 GMT, \" +\n                \"NBA-3=1234; expires=Wed, 01-Jan-2020 00:00:10 GMT; path=/xx, \" +\n                \"NBA-4=1234; expires=Wed, 01-Jan-2020 00:00:10 GMT; path=/xx; domain=.basj.es, \" +\n                \"NBA-5=1234; path=/xx; domain=.basj.es, \" +\n                \"NBA-6=1234; expires=Wed, 01-Jan-2020 00:00:10 GMT; domain=.basj.es, \" +\n                \"NBA-7=1234; expires=Wed, 01-Jan-2020 00:00:10 GMT; domain=.basj.es; comment=bla bla bla\"\n      )\n\n            .expect(\"HTTP.SETCOOKIES:cookies\",\n                \"NBA-0=, \" +\n                \"NBA-1=1234, \" +\n                \"NBA-2=1234; expires=Wed, 01-Jan-2020 00:00:10 GMT, \" +\n                \"NBA-3=1234; expires=Wed, 01-Jan-2020 00:00:10 GMT; path=/xx, \" +\n                \"NBA-4=1234; expires=Wed, 01-Jan-2020 00:00:10 GMT; path=/xx; domain=.basj.es, \" +\n                \"NBA-5=1234; path=/xx; domain=.basj.es, \" +\n                \"NBA-6=1234; expires=Wed, 01-Jan-2020 00:00:10 GMT; domain=.basj.es, \" +\n                \"NBA-7=1234; expires=Wed, 01-Jan-2020 00:00:10 GMT; domain=.basj.es; comment=bla bla bla\"\n      )\n\n            .expect(\"HTTP.SETCOOKIE:cookies.nba-0\",     \"NBA-0=\")\n            .expect(\"STRING:cookies.nba-0.value\",       \"\")\n            .expectAbsentLong(\"STRING:cookies.nba-0.expires\")\n            .expectAbsentString(\"STRING:cookies.nba-0.path\")\n            .expectAbsentString(\"STRING:cookies.nba-0.domain\")\n\n            .expect(\"HTTP.SETCOOKIE:cookies.nba-1\",     \"NBA-1=1234\")\n            .expect(\"STRING:cookies.nba-1.value\",       \"1234\")\n            .expectAbsentLong(\"STRING:cookies.nba-1.expires\")\n            .expectAbsentString(\"STRING:cookies.nba-1.path\")\n            .expectAbsentString(\"STRING:cookies.nba-1.domain\")\n\n            .expect(\"HTTP.SETCOOKIE:cookies.nba-2\",     \"NBA-2=1234; expires=Wed, 01-Jan-2020 00:00:10 GMT\")\n            .expect(\"STRING:cookies.nba-2.value\",       \"1234\")\n            .expect(\"STRING:cookies.nba-2.expires\",     \"1577836810\")\n            .expect(\"STRING:cookies.nba-2.expires\",     1577836810L)\n            .expect(\"TIME.EPOCH:cookies.nba-2.expires\", 1577836810000L)\n            .expectAbsentString(\"STRING:cookies.nba-2.path\")\n            .expectAbsentString(\"STRING:cookies.nba-2.domain\")\n\n            .expect(\"HTTP.SETCOOKIE:cookies.nba-3\",     \"NBA-3=1234; expires=Wed, 01-Jan-2020 00:00:10 GMT; path=/xx\")\n            .expect(\"STRING:cookies.nba-3.value\",       \"1234\")\n            .expect(\"STRING:cookies.nba-3.expires\",     \"1577836810\")\n            .expect(\"STRING:cookies.nba-3.expires\",     1577836810L)\n            .expect(\"TIME.EPOCH:cookies.nba-3.expires\", 1577836810000L)\n            .expect(\"STRING:cookies.nba-3.path\",        \"/xx\")\n            .expectAbsentString(\"STRING:cookies.nba-3.domain\")\n\n            .expect(\"HTTP.SETCOOKIE:cookies.nba-4\",     \"NBA-4=1234; expires=Wed, 01-Jan-2020 00:00:10 GMT; path=/xx; domain=.basj.es\")\n            .expect(\"STRING:cookies.nba-4.value\",       \"1234\")\n            .expect(\"STRING:cookies.nba-4.expires\",     \"1577836810\")\n            .expect(\"STRING:cookies.nba-4.expires\",     1577836810L)\n            .expect(\"TIME.EPOCH:cookies.nba-4.expires\", 1577836810000L)\n            .expect(\"STRING:cookies.nba-4.path\",        \"/xx\")\n            .expect(\"STRING:cookies.nba-4.domain\",      \".basj.es\")\n\n            .expect(\"HTTP.SETCOOKIE:cookies.nba-5\",     \"NBA-5=1234; path=/xx; domain=.basj.es\")\n            .expect(\"STRING:cookies.nba-5.value\",       \"1234\")\n            .expectAbsentString(\"STRING:cookies.nba-5.expires\")\n            .expectAbsentLong(\"STRING:cookies.nba-5.expires\")\n            .expectAbsentLong(\"TIME.EPOCH:cookies.nba-5.expires\")\n            .expect(\"STRING:cookies.nba-5.path\",        \"/xx\")\n            .expect(\"STRING:cookies.nba-5.domain\",      \".basj.es\")\n\n            .expect(\"HTTP.SETCOOKIE:cookies.nba-6\",     \"NBA-6=1234; expires=Wed, 01-Jan-2020 00:00:10 GMT; domain=.basj.es\")\n            .expect(\"STRING:cookies.nba-6.value\",       \"1234\")\n            .expect(\"STRING:cookies.nba-6.expires\",     \"1577836810\")\n            .expect(\"STRING:cookies.nba-6.expires\",     1577836810L)\n            .expect(\"TIME.EPOCH:cookies.nba-6.expires\", 1577836810000L)\n            .expectAbsentString(\"STRING:cookies.nba-6.path\")\n            .expect(\"STRING:cookies.nba-6.domain\",      \".basj.es\")\n\n            .expect(\"HTTP.SETCOOKIE:cookies.nba-7\",     \"NBA-7=1234; expires=Wed, 01-Jan-2020 00:00:10 GMT; domain=.basj.es; comment=bla bla bla\")\n            .expect(\"STRING:cookies.nba-7.value\",       \"1234\")\n            .expect(\"STRING:cookies.nba-7.expires\",     \"1577836810\")\n            .expect(\"STRING:cookies.nba-7.expires\",     1577836810L)\n            .expect(\"TIME.EPOCH:cookies.nba-7.expires\", 1577836810000L)\n            .expectAbsentString(\"STRING:cookies.nba-7.path\")\n            .expect(\"STRING:cookies.nba-7.domain\",      \".basj.es\")\n            .expect(\"STRING:cookies.nba-7.comment\",     \"bla bla bla\")\n\n            .checkExpectations();\n    }\n\n\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/test/java/nl/basjes/parse/httpdlog/dissectors/TestGeoIPDissectors.java",
    "content": "/*\n * Apache HTTPD logparsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage nl.basjes.parse.httpdlog.dissectors;\n\nimport nl.basjes.parse.core.Dissector;\nimport nl.basjes.parse.core.test.DissectorTester;\nimport nl.basjes.parse.core.test.TestRecord;\nimport nl.basjes.parse.httpdlog.HttpdLoglineParser;\nimport nl.basjes.parse.httpdlog.dissectors.geoip.GeoIPASNDissector;\nimport nl.basjes.parse.httpdlog.dissectors.geoip.GeoIPCityDissector;\nimport nl.basjes.parse.httpdlog.dissectors.geoip.GeoIPCountryDissector;\nimport nl.basjes.parse.httpdlog.dissectors.geoip.GeoIPISPDissector;\nimport org.junit.jupiter.api.Test;\n\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\nclass TestGeoIPDissectors {\n\n    private static final String TEST_MMDB_BASE_DIR = \"../../GeoIP2-TestData/test-data/\";\n    private static final String ASN_TEST_MMDB     = TEST_MMDB_BASE_DIR + \"GeoLite2-ASN-Test.mmdb\";\n    private static final String ISP_TEST_MMDB     = TEST_MMDB_BASE_DIR + \"GeoIP2-ISP-Test.mmdb\";\n    private static final String CITY_TEST_MMDB    = TEST_MMDB_BASE_DIR + \"GeoIP2-City-Test.mmdb\";\n    private static final String COUNTRY_TEST_MMDB = TEST_MMDB_BASE_DIR + \"GeoIP2-Country-Test.mmdb\";\n\n    DissectorTester createTester(Dissector dissector) {\n        return DissectorTester.create()\n            .withDissector(dissector)\n            .withPathPrefix(\"\");\n    }\n\n    public static class TestGeoIPDissectorsWithPrefix extends TestGeoIPDissectors {\n        // We run the SAME tests again but now wrapped in a parser that does things with a prefix.\n        DissectorTester createTester(Dissector dissector) {\n            return DissectorTester.create()\n                .withParser(new HttpdLoglineParser<>(TestRecord.class, \"%h\"))\n                .withDissector(dissector)\n                .withPathPrefix(\"connection.client.host.\");\n        }\n    }\n\n    // =================================================================================================================\n    // No such file\n    @Test\n    void testBadFileASN() {\n        AssertionError assertionError = assertThrows(AssertionError.class, ()->\n            createTester(new GeoIPASNDissector(\"Does not exist\"))\n                .withInput(\"80.100.47.45\")\n                .expect(\"ASN:asn.number\",         \"4444\")\n                .checkExpectations());\n        assertTrue(assertionError.getMessage().contains(\"Does not exist (No such file or directory)\"));\n    }\n\n    @Test\n    void testBadFileISP() {\n        AssertionError assertionError = assertThrows(AssertionError.class, () ->\n            createTester(new GeoIPISPDissector(\"Does not exist\"))\n                .withInput(\"80.100.47.45\")\n                .expect(\"ASN:asn.number\", \"4444\")\n                .checkExpectations());\n        assertTrue(assertionError.getMessage().contains(\"Does not exist (No such file or directory)\"));\n    }\n\n    @Test\n    void testBadFileCity() {\n        AssertionError assertionError = assertThrows(AssertionError.class, () ->\n            createTester(new GeoIPCityDissector(\"Does not exist\"))\n                .withInput(\"80.100.47.45\")\n                .expect(\"STRING:continent.name\", \"Europe\")\n                .checkExpectations());\n        assertTrue(assertionError.getMessage().contains(\"Does not exist (No such file or directory)\"));\n    }\n\n    @Test\n    void testBadFileCountry() {\n        AssertionError assertionError = assertThrows(AssertionError.class, () ->\n            createTester(new GeoIPCountryDissector(\"Does not exist\"))\n                .withInput(\"80.100.47.45\")\n                .expect(\"STRING:continent.name\", \"Europe\")\n                .checkExpectations());\n        assertTrue(assertionError.getMessage().contains(\"Does not exist (No such file or directory)\"));\n    }\n\n    // =================================================================================================================\n    // IP not in index\n    @Test\n    void testUnknownIPASN() {\n        createTester(new GeoIPASNDissector(ASN_TEST_MMDB))\n            .withInput(\"1.2.3.4\")\n            .expectAbsentString(\"ASN:asn.number\")\n            .checkExpectations();\n    }\n\n    @Test\n    void testUnknownIPISP() {\n        createTester(new GeoIPISPDissector(ISP_TEST_MMDB))\n            .withInput(\"1.2.3.4\")\n            .expectAbsentString(\"ASN:asn.number\")\n            .checkExpectations();\n    }\n\n    @Test\n    void testUnknownIPCity() {\n        createTester(new GeoIPCityDissector(CITY_TEST_MMDB))\n            .withInput(\"1.2.3.4\")\n            .expectAbsentString(\"STRING:continent.name\")\n            .checkExpectations();\n    }\n\n    @Test\n    void testUnknownIPCountry() {\n        createTester(new GeoIPCountryDissector(COUNTRY_TEST_MMDB))\n            .withInput(\"1.2.3.4\")\n            .expectAbsentString(\"STRING:continent.name\")\n            .checkExpectations();\n    }\n\n    // =================================================================================================================\n\n    // Tests with IPv4\n    @Test\n    void testGeoIPASN() {\n        createTester(new GeoIPASNDissector(ASN_TEST_MMDB))\n            .withInput(\"80.100.47.45\")\n            .expect(\"ASN:asn.number\",               \"4444\")\n            .expect(\"ASN:asn.number\",               4444L)\n            .expect(\"STRING:asn.organization\",      \"Basjes Global Network\")\n            .checkExpectations();\n    }\n\n    @Test\n    void testGeoIPISP() {\n        createTester(new GeoIPISPDissector(ISP_TEST_MMDB))\n            .withInput(\"80.100.47.45\")\n            .expect(\"ASN:asn.number\",               \"4444\")\n            .expect(\"ASN:asn.number\",               4444L)\n            .expect(\"STRING:asn.organization\",      \"Basjes Global Network\")\n            .expect(\"STRING:isp.name\",              \"Basjes ISP\")\n            .expect(\"STRING:isp.organization\",      \"Niels Basjes\")\n            .checkExpectations();\n    }\n\n    @Test\n    void testGeoIPCountry() {\n        createTester(new GeoIPCountryDissector(COUNTRY_TEST_MMDB))\n            .withInput(\"80.100.47.45\")\n            .expect(\"STRING:continent.name\",                \"Europe\")\n            .expect(\"STRING:continent.code\",                \"EU\")\n            .expect(\"STRING:country.name\",                  \"Netherlands\")\n            .expect(\"STRING:country.iso\",                   \"NL\")\n            .expect(\"NUMBER:country.getconfidence\",         \"42\")\n            .expect(\"NUMBER:country.getconfidence\",         42L)\n            .expect(\"BOOLEAN:country.isineuropeanunion\",    \"1\")\n            .expect(\"BOOLEAN:country.isineuropeanunion\",    1L)\n            .checkExpectations();\n    }\n\n    @Test\n    void testGeoIPCity() {\n        createTester(new GeoIPCityDissector(CITY_TEST_MMDB))\n            .withInput(\"80.100.47.45\")\n            .expect(\"STRING:continent.name\",                \"Europe\")\n            .expect(\"STRING:continent.code\",                \"EU\")\n\n            .expect(\"STRING:country.name\",                  \"Netherlands\")\n            .expect(\"STRING:country.iso\",                   \"NL\")\n            .expect(\"NUMBER:country.getconfidence\",         \"42\")\n            .expect(\"NUMBER:country.getconfidence\",         42L)\n            .expect(\"BOOLEAN:country.isineuropeanunion\",    \"1\")\n            .expect(\"BOOLEAN:country.isineuropeanunion\",    1L)\n\n            .expect(\"STRING:subdivision.name\",              \"Noord Holland\")\n            .expect(\"STRING:subdivision.iso\",               \"NH\")\n\n            .expect(\"STRING:city.name\",                     \"Amstelveen\")\n            .expect(\"NUMBER:city.confidence\",               1L)\n            .expect(\"NUMBER:city.geonameid\",                1234L)\n\n            .expect(\"STRING:postal.code\",                   \"1187\")\n            .expect(\"NUMBER:postal.confidence\",             2L)\n\n            .expect(\"STRING:location.latitude\",             \"52.5\")\n            .expect(\"STRING:location.latitude\",             52.5)\n            .expect(\"STRING:location.longitude\",            \"5.75\")\n            .expect(\"STRING:location.longitude\",            5.75)\n            .expect(\"NUMBER:location.accuracyradius\",       4L)\n\n            .checkExpectations();\n    }\n\n    // =================================================================================================================\n    // Tests with IPv6\n\n    @Test\n    void testGeoIPASNIpv6() {\n        createTester(new GeoIPASNDissector(ASN_TEST_MMDB))\n            .withInput(\"2001:980:91c0:1:21c:c0ff:fe06:e580\")\n            .expect(\"ASN:asn.number\",               \"6666\")\n            .expect(\"ASN:asn.number\",               6666L)\n            .expect(\"STRING:asn.organization\",      \"Basjes Global Network IPv6\")\n            .checkExpectations();\n    }\n\n    @Test\n    void testGeoIPISPIpv6() {\n        createTester(new GeoIPISPDissector(ISP_TEST_MMDB))\n            .withInput(\"2001:980:91c0:1:21c:c0ff:fe06:e580\")\n            .expect(\"ASN:asn.number\",               \"6666\")\n            .expect(\"ASN:asn.number\",               6666L)\n            .expect(\"STRING:asn.organization\",      \"Basjes Global Network IPv6\")\n            .expect(\"STRING:isp.name\",              \"Basjes ISP IPv6\")\n            .expect(\"STRING:isp.organization\",      \"Niels Basjes IPv6\")\n            .checkExpectations();\n    }\n\n    @Test\n    void testGeoIPCountryIpv6() {\n        createTester(new GeoIPCountryDissector(COUNTRY_TEST_MMDB))\n            .withInput(\"2001:980:91c0:1:21c:c0ff:fe06:e580\")\n            .expect(\"STRING:continent.name\",                    \"Europe\")\n            .expect(\"STRING:continent.code\",                    \"EU\")\n            .expect(\"STRING:country.name\",                      \"Netherlands\")\n            .expect(\"STRING:country.iso\",                       \"NL\")\n            .expect(\"NUMBER:country.getconfidence\",             \"42\")\n            .expect(\"NUMBER:country.getconfidence\",             42L)\n            .expect(\"BOOLEAN:country.isineuropeanunion\",        1L)\n            .expect(\"BOOLEAN:country.isineuropeanunion\",        \"1\")\n            .checkExpectations();\n    }\n\n    @Test\n    void testGeoIPCityIpv6() {\n        createTester(new GeoIPCityDissector(CITY_TEST_MMDB))\n            .withInput(\"2001:980:91c0:1:21c:c0ff:fe06:e580\")\n            .expect(\"STRING:continent.name\",                    \"Europe\")\n            .expect(\"STRING:continent.code\",                    \"EU\")\n\n            .expect(\"STRING:country.name\",                      \"Netherlands\")\n            .expect(\"STRING:country.iso\",                       \"NL\")\n            .expect(\"NUMBER:country.getconfidence\",             \"42\")\n            .expect(\"NUMBER:country.getconfidence\",             42L)\n            .expect(\"BOOLEAN:country.isineuropeanunion\",        \"1\")\n            .expect(\"BOOLEAN:country.isineuropeanunion\",        1L)\n\n            .expect(\"STRING:subdivision.name\",                  \"Noord Holland\")\n            .expect(\"STRING:subdivision.iso\",                   \"NH\")\n\n            .expect(\"STRING:city.name\",                         \"Amstelveen\")\n            .expect(\"NUMBER:city.confidence\",                   11L)\n            .expect(\"NUMBER:city.geonameid\",                    1234L)\n\n            .expect(\"STRING:postal.code\",                       \"1187\")\n            .expect(\"NUMBER:postal.confidence\",                 12L)\n\n            .expect(\"STRING:location.latitude\",                 \"52.5\")\n            .expect(\"STRING:location.latitude\",                 52.5)\n            .expect(\"STRING:location.longitude\",                \"5.75\")\n            .expect(\"STRING:location.longitude\",                5.75)\n            .expect(\"STRING:location.timezone\",                 \"Europe/Amsterdam\")\n            .expect(\"NUMBER:location.accuracyradius\",           14L)\n            .checkExpectations();\n    }\n\n    // =================================================================================================================\n    // Tests with localhost ... which is NOT in the database\n\n    @Test\n    void testGeoIPISPLocalhost() {\n        createTester(new GeoIPISPDissector(ISP_TEST_MMDB))\n            .withInput(\"127.0.0.1\")\n            .expectAbsentString(\"ASN:asn.number\")\n            .expectAbsentLong(\"ASN:asn.number\")\n            .expectAbsentString(\"STRING:asn.organization\")\n            .expectAbsentString(\"STRING:isp.name\")\n            .expectAbsentString(\"STRING:isp.organization\")\n            .checkExpectations();\n    }\n\n    @Test\n    void testGeoIPASNLocalhost() {\n        createTester(new GeoIPASNDissector(ASN_TEST_MMDB))\n            .withInput(\"127.0.0.1\")\n            .expectAbsentString(\"ASN:asn.number\")\n            .expectAbsentLong(\"ASN:asn.number\")\n            .expectAbsentString(\"STRING:asn.organization\")\n            .checkExpectations();\n    }\n\n    @Test\n    void testGeoIPCountryLocalhost() {\n        createTester(new GeoIPCountryDissector(COUNTRY_TEST_MMDB))\n            .withInput(\"127.0.0.1\")\n            .expectAbsentString(\"STRING:continent.name\")\n            .expectAbsentString(\"STRING:continent.code\")\n            .expectAbsentString(\"STRING:country.name\")\n            .expectAbsentString(\"STRING:country.iso\")\n            .expectAbsentString(\"NUMBER:country.getconfidence\")\n            .expectAbsentLong(\"NUMBER:country.getconfidence\")\n            .expectAbsentString(\"BOOLEAN:country.isineuropeanunion\")\n            .expectAbsentLong(\"BOOLEAN:country.isineuropeanunion\")\n            .checkExpectations();\n    }\n\n    @Test\n    void testGeoIPCityLocalhost() {\n        createTester(new GeoIPCityDissector(CITY_TEST_MMDB))\n            .withInput(\"127.0.0.1\")\n            .expectAbsentString(\"STRING:continent.name\")\n            .expectAbsentString(\"STRING:continent.code\")\n            .expectAbsentString(\"STRING:country.name\")\n            .expectAbsentString(\"STRING:country.iso\")\n            .expectAbsentString(\"NUMBER:country.getconfidence\")\n            .expectAbsentLong(\"NUMBER:country.getconfidence\")\n            .expectAbsentString(\"BOOLEAN:country.isineuropeanunion\")\n            .expectAbsentLong(\"BOOLEAN:country.isineuropeanunion\")\n            .expectAbsentString(\"STRING:city.name\")\n            .expectAbsentString(\"STRING:postal.code\")\n            .expectAbsentString(\"STRING:location.latitude\")\n            .expectAbsentDouble(\"STRING:location.latitude\")\n            .expectAbsentString(\"STRING:location.longitude\")\n            .expectAbsentDouble(\"STRING:location.longitude\")\n            .checkExpectations();\n    }\n\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/test/java/nl/basjes/parse/httpdlog/dissectors/TestHttpFirstLineDissector.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage nl.basjes.parse.httpdlog.dissectors;\n\nimport nl.basjes.parse.core.test.DissectorTester;\nimport org.junit.jupiter.api.Test;\n\nclass TestHttpFirstLineDissector {\n    @Test\n    void testNormal() {\n        DissectorTester.create()\n            .withDissector(new HttpFirstLineDissector())\n            .withDissector(new HttpFirstLineProtocolDissector())\n            .withInput(\"GET /index.html HTTP/1.1\")\n            .expect(\"HTTP.METHOD:method\",                     \"GET\")\n            .expect(\"HTTP.URI:uri\",                           \"/index.html\")\n            .expect(\"HTTP.PROTOCOL:protocol\",                 \"HTTP\")\n            .expect(\"HTTP.PROTOCOL.VERSION:protocol.version\", \"1.1\")\n            .checkExpectations();\n    }\n\n    @Test\n    void testChoppedFirstLine() {\n        DissectorTester.create()\n            .withDissector(new HttpFirstLineDissector())\n            .withDissector(new HttpFirstLineProtocolDissector())\n            .withInput(\"GET /index.html HTT\")\n            .expect(\"HTTP.METHOD:method\",                     \"GET\")\n            .expect(\"HTTP.URI:uri\",                           \"/index.html HTT\")\n            .expectAbsentString(\"HTTP.PROTOCOL:protocol\")\n            .expectAbsentString(\"HTTP.PROTOCOL.VERSION:protocol.version\")\n            .checkExpectations();\n    }\n\n    @Test\n    void testInvalidFirstLine() {\n        DissectorTester.create()\n            .withDissector(new HttpFirstLineDissector())\n            .withInput(\"\\\\x16\\\\x03\\\\x01\")\n            .expectAbsentString(\"HTTP.METHOD:method\")\n            .expectAbsentString(\"HTTP.URI:uri\")\n            .checkExpectations();\n    }\n\n    @Test\n    void testStrangeCommandVersionControl() {\n        DissectorTester.create()\n            .withDissector(new HttpFirstLineDissector())\n            .withDissector(new HttpFirstLineProtocolDissector())\n            .withInput(\"VERSION-CONTROL /index.html HTTP/1.1\")\n            .expect(\"HTTP.METHOD:method\",                     \"VERSION-CONTROL\")\n            .expect(\"HTTP.URI:uri\",                           \"/index.html\")\n            .expect(\"HTTP.PROTOCOL:protocol\",                 \"HTTP\")\n            .expect(\"HTTP.PROTOCOL.VERSION:protocol.version\", \"1.1\")\n            .checkExpectations();\n    }\n\n    @Test\n    void testProtocol() {\n        DissectorTester.create()\n            .withDissector(\"protocol\", new HttpFirstLineProtocolDissector())\n            .withInput(\"FOO/1.2\")\n            .expect(\"HTTP.PROTOCOL:protocol\",                 \"FOO\")\n            .expect(\"HTTP.PROTOCOL.VERSION:protocol.version\", \"1.2\")\n            .checkExpectations();\n    }\n\n    @Test\n    void testChoppedProtocol() {\n        DissectorTester.create()\n            .withDissector(\"protocol\", new HttpFirstLineProtocolDissector())\n            .withInput(\"FOO\")\n            .expect(\"HTTP.PROTOCOL:protocol\",                 (String)null)\n            .expect(\"HTTP.PROTOCOL.VERSION:protocol.version\", (String)null)\n            .checkExpectations();\n    }\n\n    @Test\n    void testEmptyProtocol1() {\n        DissectorTester.create()\n            .withDissector(\"protocol\", new HttpFirstLineProtocolDissector())\n            .withInput(\"\")\n            .expectAbsentString(\"HTTP.PROTOCOL:protocol\")\n            .expectAbsentString(\"HTTP.PROTOCOL.VERSION:protocol.version\")\n            .checkExpectations();\n    }\n\n    @Test\n    void testEmptyProtocol2() {\n        DissectorTester.create()\n            .withDissector(\"protocol\", new HttpFirstLineProtocolDissector())\n            .withInput(\"-\")\n            .expectAbsentString(\"HTTP.PROTOCOL:protocol\")\n            .expectAbsentString(\"HTTP.PROTOCOL.VERSION:protocol.version\")\n            .checkExpectations();\n    }\n\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/test/java/nl/basjes/parse/httpdlog/dissectors/TestHttpUriDissector.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage nl.basjes.parse.httpdlog.dissectors;\n\nimport nl.basjes.parse.core.test.DissectorTester;\nimport org.junit.jupiter.api.Test;\n\nclass TestHttpUriDissector {\n\n    @Test\n    void testFullUrl1() {\n        DissectorTester.create()\n            .withDissector(new HttpUriDissector())\n\n            .withInput(\"http://www.example.com/some/thing/else/index.html?foofoo=bar%20bar\")\n\n            .expect(\"HTTP.PROTOCOL:protocol\",    \"http\")\n            .expectAbsentString(\"HTTP.USERINFO:userinfo\")\n            .expect(\"HTTP.HOST:host\",            \"www.example.com\")\n            .expectAbsentString(\"HTTP.PORT:port\")\n            .expect(\"HTTP.PATH:path\",            \"/some/thing/else/index.html\")\n            .expect(\"HTTP.QUERYSTRING:query\",    \"&foofoo=bar%20bar\")\n            .expectAbsentString(\"HTTP.REF:ref\")\n            .checkExpectations();\n    }\n\n    @Test\n    void testFullUrl2() {\n        DissectorTester.create()\n            .withDissector(new HttpUriDissector())\n\n            .withInput(\"http://www.example.com/some/thing/else/index.html&aap=noot?foofoo=barbar&\")\n\n            .expect(\"HTTP.PROTOCOL:protocol\",    \"http\")\n            .expectAbsentString(\"HTTP.USERINFO:userinfo\")\n            .expect(\"HTTP.HOST:host\",            \"www.example.com\")\n            .expectAbsentString(\"HTTP.PORT:port\")\n            .expect(\"HTTP.PATH:path\",            \"/some/thing/else/index.html\")\n            .expect(\"HTTP.QUERYSTRING:query\",    \"&aap=noot&foofoo=barbar&\")\n            .expectAbsentString(\"HTTP.REF:ref\")\n            .checkExpectations();\n    }\n\n    @Test\n    void testFullUrl3() {\n        DissectorTester.create()\n            .withDissector(new HttpUriDissector())\n\n            .withInput(\"http://www.example.com:8080/some/thing/else/index.html&aap=noot?foofoo=barbar&#blabla\")\n\n            .expect(\"HTTP.PROTOCOL:protocol\",    \"http\")\n            .expectAbsentString(\"HTTP.USERINFO:userinfo\")\n            .expect(\"HTTP.HOST:host\",            \"www.example.com\")\n            .expect(\"HTTP.PORT:port\",            \"8080\")\n            .expect(\"HTTP.PATH:path\",            \"/some/thing/else/index.html\")\n            .expect(\"HTTP.QUERYSTRING:query\",    \"&aap=noot&foofoo=barbar&\")\n            .expect(\"HTTP.REF:ref\",              \"blabla\")\n            .checkExpectations();\n    }\n\n    @Test\n    void testFullUrl4() {\n        DissectorTester.create()\n            .withDissector(new HttpUriDissector())\n\n            .withInput(\"/some/thing/else/index.html?foofoo=barbar#blabla\")\n\n            .expectAbsentString(\"HTTP.PROTOCOL:protocol\")\n            .expectAbsentString(\"HTTP.USERINFO:userinfo\")\n            .expectAbsentString(\"HTTP.HOST:host\")\n            .expectAbsentString(\"HTTP.PORT:port\")\n            .expect(\"HTTP.PATH:path\",            \"/some/thing/else/index.html\")\n            .expect(\"HTTP.QUERYSTRING:query\",    \"&foofoo=barbar\")\n            .expect(\"HTTP.REF:ref\",              \"blabla\")\n            .checkExpectations();\n    }\n\n    @Test\n    void testFullUrl5() {\n        DissectorTester.create()\n            .withDissector(new HttpUriDissector())\n\n            .withInput(\"/some/thing/else/index.html&aap=noot?foofoo=bar%20bar&#bla%20bla\")\n\n            .expectAbsentString(\"HTTP.PROTOCOL:protocol\")\n            .expectAbsentString(\"HTTP.USERINFO:userinfo\")\n            .expectAbsentString(\"HTTP.HOST:host\")\n            .expectAbsentString(\"HTTP.PORT:port\")\n            .expect(\"HTTP.PATH:path\",            \"/some/thing/else/index.html\")\n            .expect(\"HTTP.QUERYSTRING:query\",    \"&aap=noot&foofoo=bar%20bar&\")\n            .expect(\"HTTP.REF:ref\",              \"bla bla\")\n            .checkExpectations();\n    }\n\n    @Test\n    void testAndroidApp1() {\n        DissectorTester.create()\n            .withDissector(new HttpUriDissector())\n\n            .withInput(\"android-app://com.google.android.googlequicksearchbox\")\n\n            .expect(\"HTTP.PROTOCOL:protocol\",    \"android-app\")\n            .expectAbsentString(\"HTTP.USERINFO:userinfo\")\n            .expect(\"HTTP.HOST:host\",            \"com.google.android.googlequicksearchbox\")\n            .expectAbsentString(\"HTTP.PORT:port\")\n            .expectAbsentString(\"HTTP.PATH:path\")\n            .expectAbsentString(\"HTTP.QUERYSTRING:query\")\n            .expectAbsentString(\"HTTP.REF:ref\")\n            .checkExpectations();\n    }\n\n    @Test\n    void testAndroidApp2() {\n        DissectorTester.create()\n            .withDissector(new HttpUriDissector())\n\n            .withInput(\"android-app://com.google.android.googlequicksearchbox/https/www.google.com\")\n\n            .expect(\"HTTP.PROTOCOL:protocol\",    \"android-app\")\n            .expectAbsentString(\"HTTP.USERINFO:userinfo\")\n            .expect(\"HTTP.HOST:host\",            \"com.google.android.googlequicksearchbox\")\n            .expectAbsentString(\"HTTP.PORT:port\")\n            .expect(\"HTTP.PATH:path\",            \"/https/www.google.com\")\n            .expectAbsentString(\"HTTP.QUERYSTRING:query\")\n            .expectAbsentString(\"HTTP.REF:ref\")\n            .checkExpectations();\n    }\n\n    @Test\n    void testBadURI() {\n        DissectorTester.create()\n            .withDissector(new HttpUriDissector())\n\n            .withInput(\"/some/thing/else/[index.html&aap=noot?foofoo=bar%20bar #bla%20bla \")\n            // Java URI parser fails on '[' here.\n\n            .expectAbsentString(\"HTTP.PROTOCOL:protocol\")\n            .expectAbsentString(\"HTTP.USERINFO:userinfo\")\n            .expectAbsentString(\"HTTP.HOST:host\")\n            .expectAbsentString(\"HTTP.PORT:port\")\n            .expect(\"HTTP.PATH:path\",            \"/some/thing/else/[index.html\")\n            .expect(\"HTTP.QUERYSTRING:query\",    \"&aap=noot&foofoo=bar%20bar%20\")\n            .expect(\"HTTP.REF:ref\",              \"bla bla \")\n            .checkExpectations();\n    }\n\n    @Test\n    void testBadURIEncoding() {\n        DissectorTester.create()\n            .withDissector(new HttpUriDissector())\n\n            // Java URI parser fails on 'Malformed escape pair'\n            .withInput(\"/index.html&promo=Give-50%-discount&promo=And-do-%Another-Wrong&last=also bad %#bla%20bla \")\n            //                              here ^              and here ^                   and here ^\n\n            .expectAbsentString(\"HTTP.PROTOCOL:protocol\")\n            .expectAbsentString(\"HTTP.USERINFO:userinfo\")\n            .expectAbsentString(\"HTTP.HOST:host\")\n            .expectAbsentString(\"HTTP.PORT:port\")\n            .expect(\"HTTP.PATH:path\",            \"/index.html\")\n            .expect(\"HTTP.QUERYSTRING:query\",    \"&promo=Give-50%25-discount&promo=And-do-%25Another-Wrong&last=also%20bad%20%25\")\n            .expect(\"HTTP.REF:ref\",              \"bla bla \")\n            .checkExpectations();\n    }\n\n    @Test\n    void testBadURIMultiPercentEncoding() {\n        DissectorTester.create()\n            .withDissector(new HttpUriDissector())\n            .withDissector(new QueryStringFieldDissector())\n\n            .withInput(\"/index.html?Linkid=%%%3dv(%40Foo)%3d%%%&emcid=B%ar\")\n\n            .expectAbsentString(\"HTTP.PROTOCOL:protocol\")\n            .expectAbsentString(\"HTTP.USERINFO:userinfo\")\n            .expectAbsentString(\"HTTP.HOST:host\")\n            .expectAbsentString(\"HTTP.PORT:port\")\n            .expect(\"HTTP.PATH:path\",            \"/index.html\")\n            .expect(\"HTTP.QUERYSTRING:query\",    \"&Linkid=%25%25%3dv(%40Foo)%3d%25%25%25&emcid=B%25ar\")\n            .expect(\"STRING:query.linkid\",       \"%%=v(@Foo)=%%%\")\n            .expectAbsentString(\"HTTP.REF:ref\")\n            .checkExpectations();\n    }\n\n    @Test\n    void testDoubleHashes() {\n        DissectorTester.create()\n            .withDissector(new HttpUriDissector())\n\n            .withInput(\"https://www.basjes.nl/#foo#bar#bazz#bla#bla#\")\n            .withInput(\"https://www.basjes.nl/path/?s2a=&Referrer=ADV1234#product_title&f=API&subid=?s2a=#product_title&name=12341234\")\n            .withInput(\"https://www.basjes.nl/path/?Referrer=ADV1234#&f=API&subid=#&name=12341234\")\n            .withInput(\"https://www.basjes.nl/path?sort&#x3D;price&filter&#x3D;new&sortOrder&#x3D;asc\")\n            .withInput(\"https://www.basjes.nl/login.html?redirectUrl=https%3A%2F%2Fwww.basjes.nl%2Faccount%2Findex.html\" +\n                       \"&_requestid=1234#x3D;12341234&Referrer&#x3D;ENTblablabla\")\n            .expect(\"HTTP.HOST:host\", \"www.basjes.nl\")\n            .checkExpectations();\n    }\n\n    @Test\n    void testHTMLEntities() {\n        DissectorTester.create()\n            .withDissector(new HttpUriDissector())\n            .withDissector(new QueryStringFieldDissector())\n\n            .withInput(\"https://www.basjes.nl/?utm_campaign=aaaa&utm_source=bbbb&utm_medium=email&utm_content=&gt;&euro;&foo;%2010x%20foo%20bar\")\n\n            .expect(\"HTTP.HOST:host\",               \"www.basjes.nl\")\n            // Note that the bad HTML entities have been converted into something \"less bad\"\n            .expect(\"HTTP.QUERYSTRING:query\",       \"&utm_campaign=aaaa&utm_source=bbbb&utm_medium=email&utm_content=%3E%E2%82%AC*foo;%2010x%20foo%20bar\")\n            .expect(\"STRING:query.utm_campaign\",    \"aaaa\")\n            .expect(\"STRING:query.utm_source\",      \"bbbb\")\n            .expect(\"STRING:query.utm_medium\",      \"email\")\n            .expect(\"STRING:query.utm_content\",     \">€*foo; 10x foo bar\")\n            .checkExpectations();\n    }\n\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/test/java/nl/basjes/parse/httpdlog/dissectors/TestModUniqueIdDissector.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage nl.basjes.parse.httpdlog.dissectors;\n\nimport nl.basjes.parse.core.test.DissectorTester;\nimport org.junit.jupiter.api.Test;\n\nclass TestModUniqueIdDissector {\n\n    @Test\n    void testUniqueId1() {\n        // This test case was verified using https://github.com/web-online/mod-unique-id-decode\n        //$ ./mod_unique_id_uudecoder -i VaGTKApid0AAALpaNo0AAAAC\n        //unique_id.stamp = Sun Jul 12 00:05:28 2015\n        //unique_id.in_addr = 10.98.119.64\n        //unique_id.pid = 47706\n        //unique_id.counter = 13965\n        //unique_id.threadIndex = 2\n\n        DissectorTester.create()\n            .withDissector(new ModUniqueIdDissector())\n            .withInput(\"VaGTKApid0AAALpaNo0AAAAC\")\n            .expect(\"TIME.EPOCH:epoch\",           \"1436652328000\")\n            .expect(\"IP:ip\",                      \"10.98.119.64\")\n            .expect(\"PROCESSID:processid\",        \"47706\")\n            .expect(\"COUNTER:counter\",            \"13965\")\n            .expect(\"THREAD_INDEX:threadindex\",   \"2\")\n            .checkExpectations();\n    }\n\n    @Test\n    void testUniqueId2() {\n        // This test case was verified using https://github.com/web-online/mod-unique-id-decode\n        //$ ./mod_unique_id_uudecoder -i Ucdv38CoEJwAAEusp6EAAADz\n        //unique_id.stamp = Sun Jun 23 23:59:59 2013\n        //unique_id.in_addr = 192.168.16.156\n        //unique_id.pid = 19372\n        //unique_id.counter = 42913\n        //unique_id.threadIndex = 243\n\n        DissectorTester.create()\n            .withDissector(new ModUniqueIdDissector())\n            .withInput(\"Ucdv38CoEJwAAEusp6EAAADz\")\n            .expect(\"TIME.EPOCH:epoch\",           \"1372024799000\")\n            .expect(\"IP:ip\",                      \"192.168.16.156\")\n            .expect(\"PROCESSID:processid\",        \"19372\")\n            .expect(\"COUNTER:counter\",            \"42913\")\n            .expect(\"THREAD_INDEX:threadindex\",   \"243\")\n            .checkExpectations();\n    }\n\n    @Test\n    void testBadUniqueIdTooShort() {\n        DissectorTester.create()\n            .withDissector(new ModUniqueIdDissector())\n            .withInput(\"Ucdv38CoEJwAAEusp6EAAAD\") // BAD: 1 letter too short\n            .expectAbsentString(\"TIME.EPOCH:epoch\")\n            .expectAbsentString(\"IP:ip\")\n            .expectAbsentString(\"PROCESSID:processid\")\n            .expectAbsentString(\"COUNTER:counter\")\n            .expectAbsentString(\"THREAD_INDEX:threadindex\")\n            .checkExpectations();\n    }\n\n    @Test\n    void testBadUniqueIdNotBase64() {\n        DissectorTester.create()\n            .withDissector(new ModUniqueIdDissector())\n            .withInput(\"Ucdv38CoEJwAAEusp6EAAAD!\") // BAD: 1 letter wrong\n            .expectAbsentString(\"TIME.EPOCH:epoch\")\n            .expectAbsentString(\"IP:ip\")\n            .expectAbsentString(\"PROCESSID:processid\")\n            .expectAbsentString(\"COUNTER:counter\")\n            .expectAbsentString(\"THREAD_INDEX:threadindex\")\n            .checkExpectations();\n    }\n\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/test/java/nl/basjes/parse/httpdlog/dissectors/TestQueryStringDissector.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage nl.basjes.parse.httpdlog.dissectors;\n\nimport nl.basjes.parse.core.test.DissectorTester;\nimport org.junit.jupiter.api.Test;\n\nclass TestQueryStringDissector {\n\n    @Test\n    void testQueryString() {\n        DissectorTester.create()\n            .withDissector(new HttpUriDissector())\n            .withDissector(new QueryStringFieldDissector())\n\n            .withInput(\"/some/thing/else/index.html&aap=1&noot=&mies&\")\n\n            .expect(\"HTTP.PATH:path\",            \"/some/thing/else/index.html\")\n            .expect(\"HTTP.QUERYSTRING:query\",    \"&aap=1&noot=&mies&\")\n            .expect(\"STRING:query.aap\",          \"1\")           // Present with value\n            .expect(\"STRING:query.noot\",         \"\")            // Present without value\n            .expect(\"STRING:query.mies\",         \"\")            // Present without value\n            .expectAbsentString(\"STRING:query.wim\")  // NOT Present\n\n            .checkExpectations();\n    }\n\n\n\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/test/java/nl/basjes/parse/httpdlog/dissectors/TestTimeStampDissector.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage nl.basjes.parse.httpdlog.dissectors;\n\nimport nl.basjes.parse.core.test.DissectorTester;\nimport nl.basjes.parse.httpdlog.HttpdLogFormatDissector;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.Arguments;\nimport org.junit.jupiter.params.provider.MethodSource;\n\nimport java.time.DateTimeException;\nimport java.time.LocalDateTime;\nimport java.time.ZoneId;\nimport java.time.ZonedDateTime;\nimport java.time.format.DateTimeFormatter;\nimport java.time.temporal.WeekFields;\nimport java.util.Locale;\nimport java.util.stream.Stream;\n\nimport static java.util.Locale.ENGLISH;\nimport static java.util.Locale.ROOT;\nimport static java.util.Locale.UK;\nimport static java.util.Locale.US;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.api.Assertions.fail;\n\n// CHECKSTYLE.OFF: LineLength\nclass TestTimeStampDissector {\n\n    @Test\n    void ensureDefaultLocaleFollowsISOWeekFields() {\n        WeekFields localeWeekFields = WeekFields.of(new TimeStampDissector().getLocale());\n        WeekFields isoWeekFields    = WeekFields.ISO;\n        assertEquals(localeWeekFields.getFirstDayOfWeek(), isoWeekFields.getFirstDayOfWeek());\n        assertEquals(localeWeekFields.getMinimalDaysInFirstWeek(), isoWeekFields.getMinimalDaysInFirstWeek());\n    }\n\n    @Test\n    void testTimeStampDissector() {\n        DissectorTester.create()\n            .withDissector(new TimeStampDissector())\n            .withInput(\"31/Dec/2012:23:00:44 -0700\")\n\n            .expect(\"TIME.EPOCH:epoch\",             \"1357020044000\")\n            .expect(\"TIME.EPOCH:epoch\",             1357020044000L)\n            .expect(\"TIME.YEAR:year\",               \"2012\")\n            .expect(\"TIME.YEAR:year\",               2012L)\n            .expect(\"TIME.MONTH:month\",             \"12\")\n            .expect(\"TIME.MONTH:month\",             12L)\n            .expect(\"TIME.MONTHNAME:monthname\",     \"December\")\n            .expect(\"TIME.DAY:day\",                 \"31\")\n            .expect(\"TIME.DAY:day\",                 31L)\n            .expect(\"TIME.HOUR:hour\",               \"23\")\n            .expect(\"TIME.HOUR:hour\",               23L)\n            .expect(\"TIME.MINUTE:minute\",           \"0\")\n            .expect(\"TIME.MINUTE:minute\",           0L)\n            .expect(\"TIME.SECOND:second\",           \"44\")\n            .expect(\"TIME.SECOND:second\",           44L)\n            .expect(\"TIME.DATE:date\",               \"2012-12-31\")\n            .expect(\"TIME.TIME:time\",               \"23:00:44\")\n            .expect(\"TIME.ZONE:timezone\",           \"-07:00\")\n            .expect(\"TIME.YEAR:year_utc\",           \"2013\")\n            .expect(\"TIME.YEAR:year_utc\",           2013L)\n            .expect(\"TIME.MONTH:month_utc\",         \"1\")\n            .expect(\"TIME.MONTH:month_utc\",         1L)\n            .expect(\"TIME.MONTHNAME:monthname_utc\", \"January\")\n            .expect(\"TIME.DAY:day_utc\",             \"1\")\n            .expect(\"TIME.DAY:day_utc\",             1L)\n            .expect(\"TIME.HOUR:hour_utc\",           \"6\")\n            .expect(\"TIME.HOUR:hour_utc\",           6L)\n            .expect(\"TIME.MINUTE:minute_utc\",       \"0\")\n            .expect(\"TIME.MINUTE:minute_utc\",       0L)\n            .expect(\"TIME.SECOND:second_utc\",       \"44\")\n            .expect(\"TIME.SECOND:second_utc\",       44L)\n            .expect(\"TIME.DATE:date_utc\",           \"2013-01-01\")\n            .expect(\"TIME.TIME:time_utc\",           \"06:00:44\")\n\n            .checkExpectations();\n    }\n\n    @Test\n    void testTimeStampDissectorPossibles() {\n        DissectorTester.create()\n            .withDissector(new TimeStampDissector())\n\n            .expectPossible(\"TIME.EPOCH:epoch\")\n            .expectPossible(\"TIME.YEAR:year\")\n            .expectPossible(\"TIME.MONTH:month\")\n            .expectPossible(\"TIME.MONTHNAME:monthname\")\n            .expectPossible(\"TIME.DAY:day\")\n            .expectPossible(\"TIME.HOUR:hour\")\n            .expectPossible(\"TIME.MINUTE:minute\")\n            .expectPossible(\"TIME.SECOND:second\")\n            .expectPossible(\"TIME.DATE:date\")\n            .expectPossible(\"TIME.TIME:time\")\n            .expectPossible(\"TIME.ZONE:timezone\")\n            .expectPossible(\"TIME.YEAR:year_utc\")\n            .expectPossible(\"TIME.MONTH:month_utc\")\n            .expectPossible(\"TIME.MONTHNAME:monthname_utc\")\n            .expectPossible(\"TIME.DAY:day_utc\")\n            .expectPossible(\"TIME.HOUR:hour_utc\")\n            .expectPossible(\"TIME.MINUTE:minute_utc\")\n            .expectPossible(\"TIME.SECOND:second_utc\")\n            .expectPossible(\"TIME.DATE:date_utc\")\n            .expectPossible(\"TIME.TIME:time_utc\")\n\n            .checkExpectations();\n    }\n\n    @Test\n    void testStrftimeStampDissectorPossibles() {\n\n        DissectorTester.create()\n            .withDissector(new HttpdLogFormatDissector(\"%{%Y-%m-%dT%H:%M:%S%z}t\"))\n            .withInput(\"2012-12-31T23:00:44-0700\")\n            .expect(\"TIME.EPOCH:request.receive.time.epoch\", \"1357020044000\")\n\n            .expectPossible(\"TIME.LOCALIZEDSTRING:request.receive.time\")\n\n            .expectPossible(\"TIME.EPOCH:request.receive.time.epoch\")\n            .expectPossible(\"TIME.YEAR:request.receive.time.year\")\n            .expectPossible(\"TIME.MONTH:request.receive.time.month\")\n            .expectPossible(\"TIME.MONTHNAME:request.receive.time.monthname\")\n            .expectPossible(\"TIME.DAY:request.receive.time.day\")\n            .expectPossible(\"TIME.HOUR:request.receive.time.hour\")\n            .expectPossible(\"TIME.MINUTE:request.receive.time.minute\")\n            .expectPossible(\"TIME.SECOND:request.receive.time.second\")\n            .expectPossible(\"TIME.DATE:request.receive.time.date\")\n            .expectPossible(\"TIME.TIME:request.receive.time.time\")\n            .expectPossible(\"TIME.ZONE:request.receive.time.timezone\")\n            .expectPossible(\"TIME.YEAR:request.receive.time.year_utc\")\n            .expectPossible(\"TIME.MONTH:request.receive.time.month_utc\")\n            .expectPossible(\"TIME.MONTHNAME:request.receive.time.monthname_utc\")\n            .expectPossible(\"TIME.DAY:request.receive.time.day_utc\")\n            .expectPossible(\"TIME.HOUR:request.receive.time.hour_utc\")\n            .expectPossible(\"TIME.MINUTE:request.receive.time.minute_utc\")\n            .expectPossible(\"TIME.SECOND:request.receive.time.second_utc\")\n            .expectPossible(\"TIME.DATE:request.receive.time.date_utc\")\n            .expectPossible(\"TIME.TIME:request.receive.time.time_utc\")\n\n            .checkExpectations();\n    }\n\n\n    @Test\n    void testTimeStamUpperLowerCaseVariations() {\n        DissectorTester.create()\n            .withDissector(new TimeStampDissector())\n\n            .withInput(\"30/sep/2016:00:00:06 +0000\")\n            .withInput(\"30/Sep/2016:00:00:06 +0000\")\n            .withInput(\"30/sEp/2016:00:00:06 +0000\")\n            .withInput(\"30/SEp/2016:00:00:06 +0000\")\n            .withInput(\"30/seP/2016:00:00:06 +0000\")\n            .withInput(\"30/SeP/2016:00:00:06 +0000\")\n            .withInput(\"30/sEP/2016:00:00:06 +0000\")\n            .withInput(\"30/SEP/2016:00:00:06 +0000\")\n\n            .expect(\"TIME.YEAR:year_utc\",       \"2016\")\n            .expect(\"TIME.MONTH:month_utc\",     \"9\")\n            .expect(\"TIME.DAY:day_utc\",         \"30\")\n\n            .checkExpectations();\n    }\n\n    private static Stream<Arguments> locales() {\n        return Stream.of(\n            Arguments.of(\"Root\",    ROOT),\n            Arguments.of(\"English\", ENGLISH),\n            Arguments.of(\"US\",      US),\n            Arguments.of(\"UK\",      UK),\n            Arguments.of(\"AU\",      new Locale(\"en\", \"AU\")),\n            Arguments.of(\"NL\",      new Locale(\"nl\", \"NL\"))\n        );\n    }\n    @ParameterizedTest(name = \"Test {index}: {0} ({1})\")\n    @MethodSource(\"locales\")\n    void testTimeStampMonthNameVariations(String name, Locale locale) {\n        DissectorTester.create()\n            .withDissector(new TimeStampDissector().setLocale(locale))\n            .withInput(\"30/jun/2016:00:00:06 +0000\")\n            .withInput(\"30/June/2016:00:00:06 +0000\")\n            .expect(\"TIME.YEAR:year_utc\",       \"2016\")\n            .expect(\"TIME.MONTH:month_utc\",     \"6\")\n            .expect(\"TIME.DAY:day_utc\",         \"30\")\n            .checkExpectations();\n\n        DissectorTester.create()\n            .withDissector(new TimeStampDissector().setLocale(locale))\n            .withInput(\"30/jul/2016:00:00:06 +0000\")\n            .withInput(\"30/July/2016:00:00:06 +0000\")\n            .expect(\"TIME.YEAR:year_utc\",       \"2016\")\n            .expect(\"TIME.MONTH:month_utc\",     \"7\")\n            .expect(\"TIME.DAY:day_utc\",         \"30\")\n            .checkExpectations();\n\n        DissectorTester.create()\n            .withDissector(new TimeStampDissector().setLocale(locale))\n            .withInput(\"30/sep/2016:00:00:06 +0000\")\n            .withInput(\"30/Sept/2016:00:00:06 +0000\")\n            .expect(\"TIME.YEAR:year_utc\",       \"2016\")\n            .expect(\"TIME.MONTH:month_utc\",     \"9\")\n            .expect(\"TIME.DAY:day_utc\",         \"30\")\n            .checkExpectations();\n\n        AssertionError dissectionFailure;\n        dissectionFailure = assertThrows(AssertionError.class, () -> {\n            DissectorTester.create()\n                .withDissector(new TimeStampDissector().setLocale(locale))\n                .withInput(\"30/xyz/2016:00:00:06 +0000\") // Intentionally bad : xyz\n                .expect(\"TIME.YEAR:year_utc\", \"2016\")\n                .expect(\"TIME.MONTH:month_utc\", \"9\")\n                .expect(\"TIME.DAY:day_utc\", \"30\")\n                .checkExpectations();\n        });\n        assertTrue(dissectionFailure.getMessage().contains(\"could not be parsed at index\"));\n\n        dissectionFailure = assertThrows(AssertionError.class, () -> {\n            DissectorTester.create()\n                .withDissector(new TimeStampDissector().setLocale(locale))\n                .withInput(\"30/sepr/2016:00:00:06 +0000\") // Intentionally bad: sepr\n                .expect(\"TIME.YEAR:year_utc\", \"2016\")\n                .expect(\"TIME.MONTH:month_utc\", \"9\")\n                .expect(\"TIME.DAY:day_utc\", \"30\")\n                .checkExpectations();\n        });\n        assertTrue(dissectionFailure.getMessage().contains(\"could not be parsed at index\"));\n    }\n\n    @Test\n    void testHandlingOfNotYetImplementedSpecialTimeFormat() {\n        // Test both the original form and the documented workaround.\n        String logformat = \"%{%Y-%m-%dT%H:%M:%S%z}t | %{timestamp}i\";\n        String input     = \"2012-12-31T23:00:44 -0700 | 2012-12-31T23:00:44 -0700\";\n\n        DissectorTester.create()\n            .withDissector(new HttpdLogFormatDissector(logformat))\n            .withInput(input)\n            .expect(\"TIME.LOCALIZEDSTRING:request.receive.time\", \"2012-12-31T23:00:44 -0700\")\n            .expect(\"HTTP.HEADER:request.header.timestamp\", \"2012-12-31T23:00:44 -0700\")\n            .checkExpectations();\n    }\n\n    @Test\n    void testSpecialTimeFormat() {\n        DissectorTester.create()\n            .withDissector(new HttpdLogFormatDissector(\"%{%Y-%m-%dT%H:%M:%S%z}t\"))\n\n            .withInput(\"2012-12-31T23:00:44-0700\")\n\n            .expect(\"TIME.EPOCH:request.receive.time.epoch\",              \"1357020044000\")\n            .expect(\"TIME.EPOCH:request.receive.time.epoch\",              1357020044000L)\n\n            .expect(\"TIME.YEAR:request.receive.time.year\",                \"2012\")\n            .expect(\"TIME.YEAR:request.receive.time.year\",                2012L)\n            .expect(\"TIME.MONTH:request.receive.time.month\",              \"12\")\n            .expect(\"TIME.MONTH:request.receive.time.month\",              12L)\n            .expect(\"TIME.MONTHNAME:request.receive.time.monthname\",      \"December\")\n            .expect(\"TIME.DAY:request.receive.time.day\",                  \"31\")\n            .expect(\"TIME.DAY:request.receive.time.day\",                  31L)\n            .expect(\"TIME.HOUR:request.receive.time.hour\",                \"23\")\n            .expect(\"TIME.HOUR:request.receive.time.hour\",                23L)\n            .expect(\"TIME.MINUTE:request.receive.time.minute\",            \"0\")\n            .expect(\"TIME.MINUTE:request.receive.time.minute\",            0L)\n            .expect(\"TIME.SECOND:request.receive.time.second\",            \"44\")\n            .expect(\"TIME.SECOND:request.receive.time.second\",            44L)\n            .expect(\"TIME.DATE:request.receive.time.date\",                \"2012-12-31\")\n            .expect(\"TIME.TIME:request.receive.time.time\",                \"23:00:44\")\n            .expect(\"TIME.ZONE:request.receive.time.timezone\",            \"-07:00\")\n\n            .expect(\"TIME.YEAR:request.receive.time.year_utc\",            \"2013\")\n            .expect(\"TIME.YEAR:request.receive.time.year_utc\",            2013L)\n            .expect(\"TIME.MONTH:request.receive.time.month_utc\",          \"1\")\n            .expect(\"TIME.MONTH:request.receive.time.month_utc\",          1L)\n            .expect(\"TIME.MONTHNAME:request.receive.time.monthname_utc\",  \"January\")\n            .expect(\"TIME.DAY:request.receive.time.day_utc\",              \"1\")\n            .expect(\"TIME.DAY:request.receive.time.day_utc\",              1L)\n            .expect(\"TIME.HOUR:request.receive.time.hour_utc\",            \"6\")\n            .expect(\"TIME.HOUR:request.receive.time.hour_utc\",            6L)\n            .expect(\"TIME.MINUTE:request.receive.time.minute_utc\",        \"0\")\n            .expect(\"TIME.MINUTE:request.receive.time.minute_utc\",        0L)\n            .expect(\"TIME.SECOND:request.receive.time.second_utc\",        \"44\")\n            .expect(\"TIME.SECOND:request.receive.time.second_utc\",        44L)\n            .expect(\"TIME.DATE:request.receive.time.date_utc\",            \"2013-01-01\")\n            .expect(\"TIME.TIME:request.receive.time.time_utc\",            \"06:00:44\")\n\n            .checkExpectations();\n    }\n\n    @Test\n    void testSpecialTimeFormatBegin() {\n        DissectorTester.create()\n            .withDissector(new HttpdLogFormatDissector(\"%{begin:%Y-%m-%dT%H:%M:%S%z}t\"))\n            .withInput(\"2012-12-31T23:00:44-0700\")\n            .expect(\"TIME.EPOCH:request.receive.time.begin.epoch\", \"1357020044000\")\n            .checkExpectations();\n    }\n\n    @Test\n    void testSpecialTimeFormatEnd() {\n        DissectorTester.create()\n            .withDissector(new HttpdLogFormatDissector(\"%{end:%Y-%m-%dT%H:%M:%S%z}t\"))\n            .withInput(\"2012-12-31T23:00:44-0700\")\n            .expect(\"TIME.EPOCH:request.receive.time.end.epoch\", \"1357020044000\")\n            .checkExpectations();\n    }\n\n    @Test\n    void testSpecialTimeFormatMultiFields1() {\n        String logline = \"12/21/16 2016-12-21 20:50 20:50:25 08:50:25 PM Wed Wednesday Dec December 21 2016 Dec 20 08 356 20  8 12 50 PM 1482349825 25 3 2016 +0100\";\n        String logformat = \"%{%D %F %R %T %r %a %A %b %B %d %G %h %H %I %j %k %l %m %M %p %s %S %u %Y %z}t\";\n\n        DissectorTester.create()\n            .withDissector(new HttpdLogFormatDissector(logformat))\n            .withInput(logline)\n            .expect(\"TIME.EPOCH:request.receive.time.epoch\",                    \"1482349825000\")\n\n            .expect(\"TIME.DATE:request.receive.time.date\",                      \"2016-12-21\")\n            .expect(\"TIME.TIME:request.receive.time.time\",                      \"20:50:25\")\n            .expect(\"TIME.YEAR:request.receive.time.year\",                      \"2016\")\n            .expect(\"TIME.MONTH:request.receive.time.month\",                    \"12\")\n            .expect(\"TIME.MONTHNAME:request.receive.time.monthname\",            \"December\")\n            .expect(\"TIME.YEAR:request.receive.time.weekyear\",                  \"2016\")\n            .expect(\"TIME.WEEK:request.receive.time.weekofweekyear\",            \"51\")\n            .expect(\"TIME.DAY:request.receive.time.day\",                        \"21\")\n            .expect(\"TIME.HOUR:request.receive.time.hour\",                      \"20\")\n            .expect(\"TIME.MINUTE:request.receive.time.minute\",                  \"50\")\n            .expect(\"TIME.SECOND:request.receive.time.second\",                  \"25\")\n            .expect(\"TIME.MILLISECOND:request.receive.time.millisecond\",        \"0\")\n            .expect(\"TIME.ZONE:request.receive.time.timezone\",                  \"+01:00\")\n\n            .expect(\"TIME.DATE:request.receive.time.date_utc\",                  \"2016-12-21\")\n            .expect(\"TIME.TIME:request.receive.time.time_utc\",                  \"19:50:25\")\n            .expect(\"TIME.YEAR:request.receive.time.year_utc\",                  \"2016\")\n            .expect(\"TIME.MONTH:request.receive.time.month_utc\",                \"12\")\n            .expect(\"TIME.MONTHNAME:request.receive.time.monthname_utc\",        \"December\")\n            .expect(\"TIME.YEAR:request.receive.time.weekyear_utc\",              \"2016\")\n            .expect(\"TIME.WEEK:request.receive.time.weekofweekyear_utc\",        \"51\")\n            .expect(\"TIME.DAY:request.receive.time.day_utc\",                    \"21\")\n            .expect(\"TIME.HOUR:request.receive.time.hour_utc\",                  \"19\")\n            .expect(\"TIME.MINUTE:request.receive.time.minute_utc\",              \"50\")\n            .expect(\"TIME.SECOND:request.receive.time.second_utc\",              \"25\")\n            .expect(\"TIME.MILLISECOND:request.receive.time.millisecond_utc\",    \"0\")\n\n            .checkExpectations();\n    }\n\n    @Test\n    void testSpecialTimeFormatMultiFields2() {\n        String logline = \"127.0.0.1 - - [22/Dec/2016:00:09:54 +0100] \\\"GET / HTTP/1.1\\\" 200 3525 \\\"12/22/16 2016-12-22 00:09 00:09:54 12:09:54 AM Thu Thursday Dec December 22 2016 Dec 00 12 357  0 12 12 09 AM 1482361794 54 4 2016 +0100\\\" \\\"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36\\\"\";\n        String logformat = \"%h %l %u %t \\\"%r\\\" %>s %O \\\"%{%D %F %R %T %r %a %A %b %B %d %G %h %H %I %j %k %l %m %M %p %s %S %u %Y %z}t\\\" \\\"%{User-Agent}i\\\"\";\n\n        DissectorTester.create()\n            .withDissector(new HttpdLogFormatDissector(logformat))\n            .withInput(logline)\n            .expect(\"TIME.EPOCH:request.receive.time.epoch\",                    \"1482361794000\")\n\n            .expect(\"TIME.DATE:request.receive.time.date\",                      \"2016-12-22\")\n            .expect(\"TIME.TIME:request.receive.time.time\",                      \"00:09:54\")\n            .expect(\"TIME.YEAR:request.receive.time.year\",                      \"2016\")\n            .expect(\"TIME.MONTH:request.receive.time.month\",                    \"12\")\n            .expect(\"TIME.MONTHNAME:request.receive.time.monthname\",            \"December\")\n            .expect(\"TIME.YEAR:request.receive.time.weekyear\",                  \"2016\")\n            .expect(\"TIME.WEEK:request.receive.time.weekofweekyear\",            \"51\")\n            .expect(\"TIME.DAY:request.receive.time.day\",                        \"22\")\n            .expect(\"TIME.HOUR:request.receive.time.hour\",                      \"0\")\n            .expect(\"TIME.MINUTE:request.receive.time.minute\",                  \"9\")\n            .expect(\"TIME.SECOND:request.receive.time.second\",                  \"54\")\n            .expect(\"TIME.MILLISECOND:request.receive.time.millisecond\",        \"0\")\n            .expect(\"TIME.ZONE:request.receive.time.timezone\",                  \"+01:00\")\n\n            .expect(\"TIME.DATE:request.receive.time.date_utc\",                  \"2016-12-21\")\n            .expect(\"TIME.TIME:request.receive.time.time_utc\",                  \"23:09:54\")\n            .expect(\"TIME.YEAR:request.receive.time.year_utc\",                  \"2016\")\n            .expect(\"TIME.MONTH:request.receive.time.month_utc\",                \"12\")\n            .expect(\"TIME.MONTHNAME:request.receive.time.monthname_utc\",        \"December\")\n            .expect(\"TIME.YEAR:request.receive.time.weekyear_utc\",              \"2016\")\n            .expect(\"TIME.WEEK:request.receive.time.weekofweekyear_utc\",        \"51\")\n            .expect(\"TIME.DAY:request.receive.time.day_utc\",                    \"21\")\n            .expect(\"TIME.HOUR:request.receive.time.hour_utc\",                  \"23\")\n            .expect(\"TIME.MINUTE:request.receive.time.minute_utc\",              \"9\")\n            .expect(\"TIME.SECOND:request.receive.time.second_utc\",              \"54\")\n            .expect(\"TIME.MILLISECOND:request.receive.time.millisecond_utc\",    \"0\")\n\n            .checkExpectations();\n    }\n\n    @Test\n    void testSpecialTimeLeadingSpaces1() {\n        String logline = \"12/21/16 2016-12-21 20:50 20:50:25 08:50:25 PM Wed Wednesday Dec December 21 2016 Dec 20 08 356 20  8 12 50 PM 1482349825 25 3 2016 +0100\";\n        String logformat = \"%{%D %F %R %T %r %a %A %b %B %d %G %h %H %I %j %k %l %m %M %p %s %S %u %Y %z}t\";\n\n        DissectorTester.create()\n            .withDissector(new HttpdLogFormatDissector(logformat))\n            .withInput(logline)\n            .expect(\"TIME.EPOCH:request.receive.time.epoch\", \"1482349825000\")\n            .checkExpectations();\n    }\n\n    @Test\n    void testSpecialTimeLeadingSpaces2a() {\n        String logline = \"127.0.0.1 - - [01/Jan/2017:13:01:21 +0100] \\\"GET / HTTP/1.1\\\" 200 3525 \\\"01/01/17 2017-01-01 13:01 13:01:21 01:01:21 PM Sun Sunday Jan January 01 2017 Jan 13 01 001 13  1 01 01 PM 21 7 2017 +0100\\\" \\\"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36\\\"\";\n        String logformat = \"%h %l %u %t \\\"%r\\\" %>s %O \\\"%{%D %F %R %T %r %a %A %b %B %d %G %h %H %I %j %k %l %m %M %p %S %u %Y %z}t\\\" \\\"%{User-Agent}i\\\"\";\n\n        DissectorTester.create()\n            .withDissector(new HttpdLogFormatDissector(logformat))\n            .withInput(logline)\n            .expect(\"TIME.EPOCH:request.receive.time.epoch\", \"1483272081000\")\n            .checkExpectations();\n    }\n\n    @Test\n    void testMultipleSpecialTime() {\n        // As described here: http://httpd.apache.org/docs/current/mod/mod_log_config.html#examples\n        // You can use the %{format}t directive multiple times to build up a time format using the extended format tokens like msec_frac:\n        // Timestamp including milliseconds\n        //          \"%{%d/%b/%Y %T}t.%{msec_frac}t %{%z}t\"\n\n        String logline   = \"01/Jan/2017 21:52:58.483 +0100\";\n\n        // The original logformat\n        // String logformat = \"%{%d/%b/%Y %T}t.%{msec_frac}t %{%z}t\";\n\n        // The transformation\n        // logformat = logformat.replaceAll(\"\\\\}t([^%{]+)%\\\\{\",\"$1\");\n\n        // The reformatted result.\n        String logformat = \"%{%d/%b/%Y %T.msec_frac %z}t\";\n\n        DissectorTester.create()\n            .withDissector(new HttpdLogFormatDissector(logformat))\n            .withInput(logline)\n            .expect(\"TIME.EPOCH:request.receive.time.epoch\", \"1483303978483\")\n            .checkExpectations();\n    }\n\n    @Test\n    void testReportedSpecialTime() {\n        String logline = \"28/feb/2017:03:39:40 +0800\";\n        String logformat = \"%{%d/%b/%Y:%H:%M:%S %z}t\";\n\n        DissectorTester.create()\n            .withDissector(new HttpdLogFormatDissector(logformat))\n            .withInput(logline)\n            .expect(\"TIME.EPOCH:request.receive.time.epoch\", \"1488224380000\")\n            .checkExpectations();\n    }\n\n    @Test\n    void testAllStrfFieldsLowValues() {\n        ZonedDateTime dateTime = ZonedDateTime.of(LocalDateTime.of(2001, 1, 2, 3, 4, 5, 678901234), ZoneId.of(\"CET\"));\n\n        checkStrfField(dateTime, \"%a\", \"Tue\");           // The abbreviated name of the day of the week according to the current locale.\n        checkStrfField(dateTime, \"%A\", \"Tuesday\");       // The full name of the day of the week according to the current locale.\n        checkStrfField(dateTime, \"%b\", \"Jan\");           // The abbreviated month name according to the current locale.\n        checkStrfField(dateTime, \"%h\", \"Jan\");           // Equivalent to %b.\n        checkStrfField(dateTime, \"%B\", \"January\");       // The full month name according to the current locale.;\n        checkStrfField(dateTime, \"%d\", \"02\");            // The day of the month as a decimal number (range 01 to 31).\n        checkStrfField(dateTime, \"%D\", \"01/02/01\");      // Equivalent to %m/%d/%y. (Yecch—for Americans only)\n        checkStrfField(dateTime, \"%e\", \" 2\");            // Like %d, the day of the month as a decimal number, but a leading zero is replaced by a space.\n        checkStrfField(dateTime, \"%F\", \"2001-01-02\");    // Equivalent to %Y-%m-%d (the ISO 8601 date format).\n        checkStrfField(dateTime, \"%G\", \"2001\");          // The ISO 8601 week-based year (see NOTES) with century as a decimal number.\n        checkStrfField(dateTime, \"%g\", \"01\");            // Like %G, but without century, that is, with a 2-digit year (00–99).\n        checkStrfField(dateTime, \"%H\", \"03\");            // The hour as a decimal number using a 24-hour clock (range 00 to 23).\n        checkStrfField(dateTime, \"%I\", \"03\");            // The hour as a decimal number using a 12-hour clock (range 01 to 12).\n        checkStrfField(dateTime, \"%j\", \"002\");           // The day of the year as a decimal number (range 001 to 366).\n        checkStrfField(dateTime, \"%k\", \" 3\");            // The hour (24-hour clock) as a decimal number (range 0 to 23); single digits are preceded by a blank. (See also %H)\n        checkStrfField(dateTime, \"%l\", \" 3\");            // The hour (12-hour clock) as a decimal number (range 1 to 12); single digits are preceded by a blank. (See also %I)\n        checkStrfField(dateTime, \"%m\", \"01\");            // The month as a decimal number (range 01 to 12).\n        checkStrfField(dateTime, \"%M\", \"04\");            // The minute as a decimal number (range 00 to 59).\n        checkStrfField(dateTime, \"%p\", \"AM\");            // Either \"AM\" or \"PM\" according to the given time value, or the corresponding strings for the current locale. Noon is treated as \"PM\" and midnight as \"AM\".\n        checkStrfField(dateTime, \"%P\", \"am\");            // Like %p but in lowercase: \"am\" or \"pm\" or a corresponding string for the current locale.\n        checkStrfField(dateTime, \"%r\", \"03:04:05 AM\");   // The time in a.m. or p.m. notation. In the POSIX locale this is equivalent to %I:%M:%S %p.\n        checkStrfField(dateTime, \"%R\", \"03:04\");         // The time in 24-hour notation (%H:%M). For a version including the seconds, see %T below.\n        checkStrfField(dateTime, \"%s\", \"978401045\");     // The number of seconds since the Epoch, 1970-01-01 00:00:00 +0000 (UTC).\n        checkStrfField(dateTime, \"%S\", \"05\");            // The second as a decimal number (range 00 to 60). (The range is up to 60 to allow for occasional leap seconds)\n        checkStrfField(dateTime, \"%T\", \"03:04:05\");      // The time in 24-hour notation (%H:%M:%S).\n        checkStrfField(dateTime, \"%u\", \"2\");             // The day of the week as a decimal, range 1 to 7, Monday being 1. See also %w.\n        checkStrfField(dateTime, \"%V\", \"1\");             // The ISO 8601 week number (see NOTES) of the current year as a decimal number, range 01 to 53, where week 1 is the first week that has at least 4 days in the new year. See also %U and %W.\n        checkStrfField(dateTime, \"%W\", \"01\");            // The week number of the current year as a decimal number, range 00 to 53, starting with the first Monday as the first day of week 01.\n        checkStrfField(dateTime, \"%y\", \"01\");            // The year as a decimal number without a century (range 00 to 99).\n        checkStrfField(dateTime, \"%Y\", \"2001\");          // The year as a decimal number including the century.\n        checkStrfField(dateTime, \"%z\", \"+0100\");         // The +hhmm or -hhmm numeric timezone.\n        checkStrfField(dateTime, \"%Z\", \"CET\");           // The timezone name or abbreviation.\n\n        checkStrfField(dateTime, \"msec_frac\", \"678\");    // Apache HTTPD specific: milliseconds fraction\n        checkStrfField(dateTime, \"usec_frac\", \"678901\"); // Apache HTTPD specific: microseconds fraction\n        checkStrfField(dateTime, \"%F %T.msec_frac %z\", \"2001-01-02 03:04:05.678 +0100\");\n        checkStrfField(dateTime, \"%F %T.usec_frac %z\", \"2001-01-02 03:04:05.678901 +0100\");\n\n        // With extra '%'\n        checkStrfField(dateTime, \"%msec_frac\", \"678\");    // Apache HTTPD specific: milliseconds fraction\n        checkStrfField(dateTime, \"%usec_frac\", \"678901\"); // Apache HTTPD specific: microseconds fraction\n        checkStrfField(dateTime, \"%F %T.msec_frac %z\", \"2001-01-02 03:04:05.678 +0100\");\n        checkStrfField(dateTime, \"%F %T.usec_frac %z\", \"2001-01-02 03:04:05.678901 +0100\");\n    }\n\n    @Test\n    void testAllStrfFieldsHighValues() {\n        ZonedDateTime dateTime = ZonedDateTime.of(LocalDateTime.of(2017, 11, 12, 23, 14, 15, 678901234), ZoneId.of(\"CET\"));\n\n        checkStrfField(dateTime, \"%a\", \"Sun\");           // The abbreviated name of the day of the week according to the current locale.\n        checkStrfField(dateTime, \"%A\", \"Sunday\");        // The full name of the day of the week according to the current locale.\n        checkStrfField(dateTime, \"%b\", \"Nov\");           // The abbreviated month name according to the current locale.\n        checkStrfField(dateTime, \"%h\", \"Nov\");           // Equivalent to %b.\n        checkStrfField(dateTime, \"%B\", \"November\");      // The full month name according to the current locale.;\n        checkStrfField(dateTime, \"%d\", \"12\");            // The day of the month as a decimal number (range 01 to 31).\n        checkStrfField(dateTime, \"%D\", \"11/12/17\");      // Equivalent to %m/%d/%y. (Yecch—for Americans only)\n        checkStrfField(dateTime, \"%e\", \"12\");            // Like %d, the day of the month as a decimal number, but a leading zero is replaced by a space.\n        checkStrfField(dateTime, \"%F\", \"2017-11-12\");    // Equivalent to %Y-%m-%d (the ISO 8601 date format).\n        checkStrfField(dateTime, \"%G\", \"2017\");          // The ISO 8601 week-based year (see NOTES) with century as a decimal number. The 4-digit year corresponding to the ISO week number (see %V). This has the same format and value as %Y, except that if the ISO week number belongs to the previous or next year, that year is used instead.\n        checkStrfField(dateTime, \"%g\", \"17\");            // Like %G, but without century, that is, with a 2-digit year (00–99).\n        checkStrfField(dateTime, \"%H\", \"23\");            // The hour as a decimal number using a 24-hour clock (range 00 to 23).\n        checkStrfField(dateTime, \"%I\", \"11\");            // The hour as a decimal number using a 12-hour clock (range 01 to 12).\n        checkStrfField(dateTime, \"%j\", \"316\");           // The day of the year as a decimal number (range 001 to 366).\n        checkStrfField(dateTime, \"%k\", \"23\");            // The hour (24-hour clock) as a decimal number (range 0 to 23); single digits are preceded by a blank. (See also %H)\n        checkStrfField(dateTime, \"%l\", \"11\");            // The hour (12-hour clock) as a decimal number (range 1 to 12); single digits are preceded by a blank. (See also %I)\n        checkStrfField(dateTime, \"%m\", \"11\");            // The month as a decimal number (range 01 to 12).\n        checkStrfField(dateTime, \"%M\", \"14\");            // The minute as a decimal number (range 00 to 59).\n        checkStrfField(dateTime, \"%p\", \"PM\");            // Either \"AM\" or \"PM\" according to the given time value, or the corresponding strings for the current locale. Noon is treated as \"PM\" and midnight as \"AM\".\n        checkStrfField(dateTime, \"%P\", \"pm\");            // Like %p but in lowercase: \"am\" or \"pm\" or a corresponding string for the current locale.\n        checkStrfField(dateTime, \"%r\", \"11:14:15 PM\");   // The time in a.m. or p.m. notation. In the POSIX locale this is equivalent to %I:%M:%S %p.\n        checkStrfField(dateTime, \"%R\", \"23:14\");         // The time in 24-hour notation (%H:%M). For a version including the seconds, see %T below.\n        checkStrfField(dateTime, \"%s\", \"1510524855\");    // The number of seconds since the Epoch, 1970-01-01 00:00:00 +0000 (UTC).\n        checkStrfField(dateTime, \"%S\", \"15\");            // The second as a decimal number (range 00 to 60). (The range is up to 60 to allow for occasional leap seconds)\n        checkStrfField(dateTime, \"%T\", \"23:14:15\");      // The time in 24-hour notation (%H:%M:%S).\n        checkStrfField(dateTime, \"%u\", \"7\");             // The day of the week as a decimal, range 1 to 7, Monday being 1. See also %w.\n        checkStrfField(dateTime, \"%V\", \"45\");            // The ISO 8601 week number (see NOTES) of the current year as a decimal number, range 01 to 53, where week 1 is the first week that has at least 4 days in the new year. See also %U and %W.\n        checkStrfField(dateTime, \"%W\", \"45\");            // The week number of the current year as a decimal number, range 00 to 53, starting with the first Monday as the first day of week 01.\n        checkStrfField(dateTime, \"%y\", \"17\");            // The year as a decimal number without a century (range 00 to 99).\n        checkStrfField(dateTime, \"%Y\", \"2017\");          // The year as a decimal number including the century.\n        checkStrfField(dateTime, \"%z\", \"+0100\");         // The +hhmm or -hhmm numeric timezone.\n        checkStrfField(dateTime, \"%Z\", \"CET\");           // The timezone name or abbreviation.\n\n        checkStrfField(dateTime, \"msec_frac\", \"678\");    // Apache HTTPD specific: milliseconds fraction\n        checkStrfField(dateTime, \"usec_frac\", \"678901\"); // Apache HTTPD specific: microseconds fraction\n        checkStrfField(dateTime, \"%F %T.msec_frac %z\", \"2017-11-12 23:14:15.678 +0100\");\n        checkStrfField(dateTime, \"%F %T.usec_frac %z\", \"2017-11-12 23:14:15.678901 +0100\");\n\n        // With extra '%'\n        checkStrfField(dateTime, \"%msec_frac\", \"678\");    // Apache HTTPD specific: milliseconds fraction\n        checkStrfField(dateTime, \"%usec_frac\", \"678901\"); // Apache HTTPD specific: microseconds fraction\n        checkStrfField(dateTime, \"%F %T.%msec_frac %z\", \"2017-11-12 23:14:15.678 +0100\");\n        checkStrfField(dateTime, \"%F %T.%usec_frac %z\", \"2017-11-12 23:14:15.678901 +0100\");\n    }\n\n\n    private void checkStrfField(ZonedDateTime dateTime, String strffield, String expected) {\n        try {\n            DateTimeFormatter dateTimeFormatter = StrfTimeToDateTimeFormatter.convert(strffield, ZoneId.of(\"CET\"));\n            assertNotNull(dateTimeFormatter);\n            String result = dateTime.format(dateTimeFormatter);\n            assertEquals(expected, result, \"Incorrect field \" + strffield);\n        } catch (DateTimeException dte) {\n            fail(\"DateTimeException for field \" + strffield + \" \"+ dte.getMessage());\n        }\n    }\n\n    @Test\n    void ensureUnsupportedFields() {\n        checkUnsupported(\"%c\"); // The preferred date and time representation for the current locale.\n        checkUnsupported(\"%C\"); // The century number (year/100) as a 2-digit integer.\n        checkUnsupported(\"%U\"); // The week number of the current year as a decimal number, range 00 to 53, starting with the first Sunday as the first day of week 01. See also %V and %W.\n        checkUnsupported(\"%w\"); // The day of the week as a decimal, range 0 to 6, Sunday being 0. See also %u.\n        checkUnsupported(\"%x\"); // The preferred date representation for the current locale without the time.\n        checkUnsupported(\"%X\"); // The preferred time representation for the current locale without the date.\n        checkUnsupported(\"%+\"); // The date and time in date(1) format.\n    }\n\n    private void checkUnsupported(String strffield) {\n        try {\n            StrfTimeToDateTimeFormatter.convert(strffield);\n        } catch (StrfTimeToDateTimeFormatter.UnsupportedStrfField e) {\n            return; // This is actually good\n        } catch (Exception e) {\n            fail(\"Unexpected exception:\" + e.getMessage());\n        }\n        fail(\"DateTimeException for field \" + strffield + \" should be unsupported\");\n    }\n\n    @Test\n    void strfTimeWithMissingTimeZone() {\n        DissectorTester.create()\n            .withDissector(new HttpdLogFormatDissector(\"%{%F %H:%M:%S}t\"))\n            .withInput(\"2017-12-25 00:00:00\")\n            .expect(\"TIME.EPOCH:request.receive.time.epoch\", 1514160000000L)\n            .checkExpectations();\n\n        String logformat = \"%a %l %u %{%F %H:%M:%S}t \\\"%r\\\" %>s %b \\\"%{Referer}i\\\" \\\"%{User-Agent}i\\\" \\\"%{Cookie}i\\\" t=%D\";\n        String logline = \"192.168.85.3 - - 2017-12-25 00:00:00 \\\"GET /up.html HTTP/1.0\\\" 203 8 \\\"-\\\" \\\"HTTP-Monitor/1.1\\\" \\\"-\\\" t=4920\";\n        DissectorTester.create()\n            .withDissector(new HttpdLogFormatDissector(logformat))\n            .withInput(logline)\n            .expect(\"TIME.EPOCH:request.receive.time.epoch\", 1514160000000L)\n            .checkExpectations();\n    }\n\n    @Test\n    void testStrfTimeMsecfrac() {\n        String logformat = \"%a %l %u %{%F %H:%M:%S.msec_frac}t \\\"%r\\\" %>s %b \\\"%{Referer}i\\\" \\\"%{User-Agent}i\\\" \\\"%{Cookie}i\\\" t=%D\";\n        String logline = \"192.168.85.3 - - 2017-12-25 00:00:42.123 \\\"GET /up.html HTTP/1.0\\\" 203 8 \\\"-\\\" \\\"HTTP-Monitor/1.1\\\" \\\"-\\\" t=4920\";\n        DissectorTester.create()\n            .withDissector(new HttpdLogFormatDissector(logformat))\n            .withInput(logline)\n            .expect(\"TIME.EPOCH:request.receive.time.epoch\", 1514160042123L)\n            .expect(\"TIME.SECOND:request.receive.time.second\", 42L)\n            .expect(\"TIME.MILLISECOND:request.receive.time.millisecond\", 123L)\n            .expect(\"TIME.MICROSECOND:request.receive.time.microsecond\", 123000L)\n            .expect(\"TIME.NANOSECOND:request.receive.time.nanosecond\", 123000000L)\n            .expect(\"TIME.MILLISECOND:request.receive.time.millisecond_utc\", 123L)\n            .expect(\"TIME.MICROSECOND:request.receive.time.microsecond_utc\", 123000L)\n            .expect(\"TIME.NANOSECOND:request.receive.time.nanosecond_utc\", 123000000L)\n            .checkExpectations();\n    }\n\n    @Test\n    void testStrfTimeMsecfracP() {\n        String logformat = \"%a %l %u %{%F %H:%M:%S.%msec_frac}t \\\"%r\\\" %>s %b \\\"%{Referer}i\\\" \\\"%{User-Agent}i\\\" \\\"%{Cookie}i\\\" t=%D\";\n        String logline = \"192.168.85.3 - - 2017-12-25 00:00:42.123 \\\"GET /up.html HTTP/1.0\\\" 203 8 \\\"-\\\" \\\"HTTP-Monitor/1.1\\\" \\\"-\\\" t=4920\";\n        DissectorTester.create()\n            .withDissector(new HttpdLogFormatDissector(logformat))\n            .withInput(logline)\n            .expect(\"TIME.EPOCH:request.receive.time.epoch\", 1514160042123L)\n            .expect(\"TIME.SECOND:request.receive.time.second\", 42L)\n            .expect(\"TIME.MILLISECOND:request.receive.time.millisecond\", 123L)\n            .expect(\"TIME.MICROSECOND:request.receive.time.microsecond\", 123000L)\n            .expect(\"TIME.NANOSECOND:request.receive.time.nanosecond\", 123000000L)\n            .expect(\"TIME.MILLISECOND:request.receive.time.millisecond_utc\", 123L)\n            .expect(\"TIME.MICROSECOND:request.receive.time.microsecond_utc\", 123000L)\n            .expect(\"TIME.NANOSECOND:request.receive.time.nanosecond_utc\", 123000000L)\n            .checkExpectations();\n    }\n\n    @Test\n    void testStrfTimeUsecfrac() {\n        String logformat = \"%a %l %u %{%F %H:%M:%S.usec_frac}t \\\"%r\\\" %>s %b \\\"%{Referer}i\\\" \\\"%{User-Agent}i\\\" \\\"%{Cookie}i\\\" t=%D\";\n        String logline = \"192.168.85.3 - - 2017-12-25 00:00:42.123456 \\\"GET /up.html HTTP/1.0\\\" 203 8 \\\"-\\\" \\\"HTTP-Monitor/1.1\\\" \\\"-\\\" t=4920\";\n        DissectorTester.create()\n            .withDissector(new HttpdLogFormatDissector(logformat))\n            .withInput(logline)\n            .expect(\"TIME.EPOCH:request.receive.time.epoch\", 1514160042123L)\n            .expect(\"TIME.SECOND:request.receive.time.second\", 42L)\n            .expect(\"TIME.MILLISECOND:request.receive.time.millisecond\", 123L)\n            .expect(\"TIME.MICROSECOND:request.receive.time.microsecond\", 123456L)\n            .expect(\"TIME.NANOSECOND:request.receive.time.nanosecond\", 123456000L)\n            .expect(\"TIME.MILLISECOND:request.receive.time.millisecond_utc\", 123L)\n            .expect(\"TIME.MICROSECOND:request.receive.time.microsecond_utc\", 123456L)\n            .expect(\"TIME.NANOSECOND:request.receive.time.nanosecond_utc\", 123456000L)\n            .checkExpectations();\n    }\n\n    @Test\n    void testStrfTimeUsecfracP() {\n        String logformat = \"%a %l %u %{%F %H:%M:%S.%usec_frac}t \\\"%r\\\" %>s %b \\\"%{Referer}i\\\" \\\"%{User-Agent}i\\\" \\\"%{Cookie}i\\\" t=%D\";\n        String logline = \"192.168.85.3 - - 2017-12-25 00:00:42.123456 \\\"GET /up.html HTTP/1.0\\\" 203 8 \\\"-\\\" \\\"HTTP-Monitor/1.1\\\" \\\"-\\\" t=4920\";\n        DissectorTester.create()\n            .withDissector(new HttpdLogFormatDissector(logformat))\n            .withInput(logline)\n            .expect(\"TIME.EPOCH:request.receive.time.epoch\", 1514160042123L)\n            .expect(\"TIME.SECOND:request.receive.time.second\", 42L)\n            .expect(\"TIME.MILLISECOND:request.receive.time.millisecond\", 123L)\n            .expect(\"TIME.MICROSECOND:request.receive.time.microsecond\", 123456L)\n            .expect(\"TIME.NANOSECOND:request.receive.time.nanosecond\", 123456000L)\n            .expect(\"TIME.MILLISECOND:request.receive.time.millisecond_utc\", 123L)\n            .expect(\"TIME.MICROSECOND:request.receive.time.microsecond_utc\", 123456L)\n            .expect(\"TIME.NANOSECOND:request.receive.time.nanosecond_utc\", 123456000L)\n            .checkExpectations();\n    }\n\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/test/java/nl/basjes/parse/httpdlog/nginxmodules/NginxAllFieldsTest.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage nl.basjes.parse.httpdlog.nginxmodules;\n\nimport nl.basjes.parse.core.test.DissectorTester;\nimport nl.basjes.parse.core.test.TestRecord;\nimport nl.basjes.parse.httpdlog.HttpdLoglineParser;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.List;\n\nimport static org.junit.jupiter.api.Assertions.assertFalse;\n\n// This test simply checks which of the fields known in the Nginx documentation have been implemented.\nclass NginxAllFieldsTest {\n    // All variables mentioned on https://nginx.org/en/docs/varindex.html\n\n    @Test\n    void ensureAllFieldsAreHandled() {\n        checkVariable(\"$arg_\");                        // ngx_http_core_module\n        checkVariable(\"$args\");                        // ngx_http_core_module\n        checkVariable(\"$binary_remote_addr\");          // ngx_http_core_module\n        checkVariable(\"$binary_remote_addr\");          // ngx_stream_core_module\n        checkVariable(\"$body_bytes_sent\");             // ngx_http_core_module\n        checkVariable(\"$bytes_received\");              // ngx_stream_core_module\n        checkVariable(\"$bytes_sent\");                  // ngx_http_core_module\n        checkVariable(\"$bytes_sent\");                  // ngx_http_log_module\n        checkVariable(\"$bytes_sent\");                  // ngx_stream_core_module\n        checkVariable(\"$connection\");                  // ngx_http_core_module\n        checkVariable(\"$connection\");                  // ngx_http_log_module\n        checkVariable(\"$connection\");                  // ngx_stream_core_module\n        checkVariable(\"$connection_requests\");         // ngx_http_core_module\n        checkVariable(\"$connection_requests\");         // ngx_http_log_module\n        checkVariable(\"$content_length\");              // ngx_http_core_module\n        checkVariable(\"$content_type\");                // ngx_http_core_module\n        checkVariable(\"$cookie_\");                     // ngx_http_core_module\n        checkVariable(\"$document_root\");               // ngx_http_core_module\n        checkVariable(\"$document_uri\");                // ngx_http_core_module\n        checkVariable(\"$host\");                        // ngx_http_core_module\n        checkVariable(\"$hostname\");                    // ngx_http_core_module\n        checkVariable(\"$hostname\");                    // ngx_stream_core_module\n        checkVariable(\"$http_\");                       // ngx_http_core_module\n        checkVariable(\"$https\");                       // ngx_http_core_module\n        checkVariable(\"$is_args\");                     // ngx_http_core_module\n        checkVariable(\"$limit_rate\");                  // ngx_http_core_module\n        checkVariable(\"$msec\");                        // ngx_http_core_module\n        checkVariable(\"$msec\");                        // ngx_http_log_module\n        checkVariable(\"$msec\");                        // ngx_stream_core_module\n        checkVariable(\"$nginx_version\");               // ngx_http_core_module\n        checkVariable(\"$nginx_version\");               // ngx_stream_core_module\n        checkVariable(\"$pid\");                         // ngx_http_core_module\n        checkVariable(\"$pid\");                         // ngx_stream_core_module\n        checkVariable(\"$pipe\");                        // ngx_http_core_module\n        checkVariable(\"$pipe\");                        // ngx_http_log_module\n        checkVariable(\"$protocol\");                    // ngx_stream_core_module\n\n        checkVariable(\"$proxy_protocol_addr\");         // ngx_http_core_module\n        checkVariable(\"$proxy_protocol_addr\");         // ngx_stream_core_module\n        checkVariable(\"$proxy_protocol_port\");         // ngx_http_core_module\n        checkVariable(\"$proxy_protocol_port\");         // ngx_stream_core_module\n        checkVariable(\"$query_string\");                // ngx_http_core_module\n        checkVariable(\"$realpath_root\");               // ngx_http_core_module\n        checkVariable(\"$remote_addr\");                 // ngx_http_core_module\n        checkVariable(\"$remote_addr\");                 // ngx_stream_core_module\n        checkVariable(\"$remote_port\");                 // ngx_http_core_module\n        checkVariable(\"$remote_port\");                 // ngx_stream_core_module\n        checkVariable(\"$remote_user\");                 // ngx_http_core_module\n        checkVariable(\"$request\");                     // ngx_http_core_module\n        checkVariable(\"$request_body\");                // ngx_http_core_module\n        checkVariable(\"$request_body_file\");           // ngx_http_core_module\n        checkVariable(\"$request_completion\");          // ngx_http_core_module\n        checkVariable(\"$request_filename\");            // ngx_http_core_module\n        checkVariable(\"$request_id\");                  // ngx_http_core_module\n        checkVariable(\"$request_length\");              // ngx_http_core_module\n        checkVariable(\"$request_length\");              // ngx_http_log_module\n        checkVariable(\"$request_method\");              // ngx_http_core_module\n        checkVariable(\"$request_time\");                // ngx_http_core_module\n        checkVariable(\"$request_time\");                // ngx_http_log_module\n        checkVariable(\"$request_uri\");                 // ngx_http_core_module\n        checkVariable(\"$scheme\");                      // ngx_http_core_module\n        checkVariable(\"$sent_http_somename\");                  // ngx_http_core_module\n        checkVariable(\"$sent_trailer_somename\");               // ngx_http_core_module\n        checkVariable(\"$server_addr\");                 // ngx_http_core_module\n        checkVariable(\"$server_addr\");                 // ngx_stream_core_module\n        checkVariable(\"$server_name\");                 // ngx_http_core_module\n        checkVariable(\"$server_port\");                 // ngx_http_core_module\n        checkVariable(\"$server_port\");                 // ngx_stream_core_module\n        checkVariable(\"$server_protocol\");             // ngx_http_core_module\n        checkVariable(\"$session_time\");                // ngx_stream_core_module\n        checkVariable(\"$status\");                      // ngx_http_core_module\n        checkVariable(\"$status\");                      // ngx_http_log_module\n        checkVariable(\"$status\");                      // ngx_stream_core_module\n        checkVariable(\"$tcpinfo_rtt\");                 // ngx_http_core_module\n        checkVariable(\"$tcpinfo_rttvar\");              // ngx_http_core_module\n        checkVariable(\"$tcpinfo_snd_cwnd\");            // ngx_http_core_module\n        checkVariable(\"$tcpinfo_rcv_space\");           // ngx_http_core_module\n        checkVariable(\"$time_iso8601\");                // ngx_http_core_module\n        checkVariable(\"$time_iso8601\");                // ngx_http_log_module\n        checkVariable(\"$time_iso8601\");                // ngx_stream_core_module\n        checkVariable(\"$time_local\");                  // ngx_http_core_module\n        checkVariable(\"$time_local\");                  // ngx_http_log_module\n        checkVariable(\"$time_local\");                  // ngx_stream_core_module\n\n        checkVariable(\"$secure_link\");                 // ngx_http_secure_link_module\n// NOT FOR LOGGING: checkVariable(\"$secure_link_expires\");         // ngx_http_secure_link_module\n\n// NOT FOR LOGGING: checkVariable(\"$session_log_binary_id\");       // ngx_http_session_log_module\n        checkVariable(\"$session_log_id\");              // ngx_http_session_log_module\n\n        checkVariable(\"$slice_range\");                 // ngx_http_slice_module\n\n        checkVariable(\"$proxy_add_x_forwarded_for\");   // ngx_http_proxy_module\n        checkVariable(\"$proxy_host\");                  // ngx_http_proxy_module\n        checkVariable(\"$proxy_port\");                  // ngx_http_proxy_module\n\n        checkVariable(\"$ssl_cipher\");                  // ngx_http_ssl_module\n        checkVariable(\"$ssl_cipher\");                  // ngx_stream_ssl_module\n        checkVariable(\"$ssl_ciphers\");                 // ngx_http_ssl_module\n        checkVariable(\"$ssl_ciphers\");                 // ngx_stream_ssl_module\n        checkVariable(\"$ssl_client_cert\");             // ngx_http_ssl_module\n        checkVariable(\"$ssl_client_cert\");             // ngx_stream_ssl_module\n        checkVariable(\"$ssl_client_escaped_cert\");     // ngx_http_ssl_module\n        checkVariable(\"$ssl_client_fingerprint\");      // ngx_http_ssl_module\n        checkVariable(\"$ssl_client_fingerprint\");      // ngx_stream_ssl_module\n        checkVariable(\"$ssl_client_i_dn\");             // ngx_http_ssl_module\n        checkVariable(\"$ssl_client_i_dn\");             // ngx_stream_ssl_module\n        checkVariable(\"$ssl_client_i_dn_legacy\");      // ngx_http_ssl_module\n        checkVariable(\"$ssl_client_raw_cert\");         // ngx_http_ssl_module\n        checkVariable(\"$ssl_client_raw_cert\");         // ngx_stream_ssl_module\n        checkVariable(\"$ssl_client_s_dn\");             // ngx_http_ssl_module\n        checkVariable(\"$ssl_client_s_dn\");             // ngx_stream_ssl_module\n        checkVariable(\"$ssl_client_s_dn_legacy\");      // ngx_http_ssl_module\n        checkVariable(\"$ssl_client_serial\");           // ngx_http_ssl_module\n        checkVariable(\"$ssl_client_serial\");           // ngx_stream_ssl_module\n        checkVariable(\"$ssl_client_v_end\");            // ngx_http_ssl_module\n        checkVariable(\"$ssl_client_v_end\");            // ngx_stream_ssl_module\n        checkVariable(\"$ssl_client_v_remain\");         // ngx_http_ssl_module\n        checkVariable(\"$ssl_client_v_remain\");         // ngx_stream_ssl_module\n        checkVariable(\"$ssl_client_v_start\");          // ngx_http_ssl_module\n        checkVariable(\"$ssl_client_v_start\");          // ngx_stream_ssl_module\n        checkVariable(\"$ssl_client_verify\");           // ngx_http_ssl_module\n        checkVariable(\"$ssl_client_verify\");           // ngx_stream_ssl_module\n        checkVariable(\"$ssl_curves\");                  // ngx_http_ssl_module\n        checkVariable(\"$ssl_curves\");                  // ngx_stream_ssl_module\n        checkVariable(\"$ssl_early_data\");              // ngx_http_ssl_module\n        checkVariable(\"$ssl_preread_alpn_protocols\");  // ngx_stream_ssl_preread_module\n        checkVariable(\"$ssl_preread_protocol\");        // ngx_stream_ssl_preread_module\n        checkVariable(\"$ssl_preread_server_name\");     // ngx_stream_ssl_preread_module\n        checkVariable(\"$ssl_protocol\");                // ngx_http_ssl_module\n        checkVariable(\"$ssl_protocol\");                // ngx_stream_ssl_module\n        checkVariable(\"$ssl_server_name\");             // ngx_http_ssl_module\n        checkVariable(\"$ssl_server_name\");             // ngx_stream_ssl_module\n        checkVariable(\"$ssl_session_id\");              // ngx_http_ssl_module\n        checkVariable(\"$ssl_session_id\");              // ngx_stream_ssl_module\n        checkVariable(\"$ssl_session_reused\");          // ngx_http_ssl_module\n        checkVariable(\"$ssl_session_reused\");          // ngx_stream_ssl_module\n\n        checkVariable(\"$upstream_addr\");               // ngx_http_upstream_module\n        checkVariable(\"$upstream_addr\");               // ngx_stream_upstream_module\n        checkVariable(\"$upstream_bytes_received\");     // ngx_http_upstream_module\n        checkVariable(\"$upstream_bytes_received\");     // ngx_stream_upstream_module\n        checkVariable(\"$upstream_bytes_sent\");         // ngx_http_upstream_module\n        checkVariable(\"$upstream_bytes_sent\");         // ngx_stream_upstream_module\n        checkVariable(\"$upstream_cache_status\");       // ngx_http_upstream_module\n        checkVariable(\"$upstream_connect_time\");       // ngx_http_upstream_module\n        checkVariable(\"$upstream_connect_time\");       // ngx_stream_upstream_module\n        checkVariable(\"$upstream_cookie_\");            // ngx_http_upstream_module\n        checkVariable(\"$upstream_first_byte_time\");    // ngx_stream_upstream_module\n        checkVariable(\"$upstream_header_time\");        // ngx_http_upstream_module\n        checkVariable(\"$upstream_http_\");              // ngx_http_upstream_module\n        checkVariable(\"$upstream_queue_time\");         // ngx_http_upstream_module\n        checkVariable(\"$upstream_response_length\");    // ngx_http_upstream_module\n        checkVariable(\"$upstream_response_time\");      // ngx_http_upstream_module\n        checkVariable(\"$upstream_session_time\");       // ngx_stream_upstream_module\n        checkVariable(\"$upstream_status\");             // ngx_http_upstream_module\n        checkVariable(\"$upstream_trailer_\");           // ngx_http_upstream_module\n        checkVariable(\"$uri\");                         // ngx_http_core_module\n\n        checkVariable(\"$uid_got\");                     // ngx_http_userid_module\n        checkVariable(\"$uid_reset\");                   // ngx_http_userid_module\n        checkVariable(\"$uid_set\");                     // ngx_http_userid_module\n\n        checkVariable(\"$ancient_browser\");             // ngx_http_browser_module\n        checkVariable(\"$modern_browser\");              // ngx_http_browser_module\n        checkVariable(\"$msie\");                        // ngx_http_browser_module\n\n        checkVariable(\"$connections_active\");          // ngx_http_stub_status_module\n        checkVariable(\"$connections_reading\");         // ngx_http_stub_status_module\n        checkVariable(\"$connections_waiting\");         // ngx_http_stub_status_module\n        checkVariable(\"$connections_writing\");         // ngx_http_stub_status_module\n\n        checkVariable(\"$date_gmt\");                    // ngx_http_ssi_module\n        checkVariable(\"$date_local\");                  // ngx_http_ssi_module\n\n        checkVariable(\"$fastcgi_path_info\");           // ngx_http_fastcgi_module\n        checkVariable(\"$fastcgi_script_name\");         // ngx_http_fastcgi_module\n\n        checkVariable(\"$geoip_area_code\");             // ngx_http_geoip_module\n        checkVariable(\"$geoip_area_code\");             // ngx_stream_geoip_module\n        checkVariable(\"$geoip_city\");                  // ngx_http_geoip_module\n        checkVariable(\"$geoip_city\");                  // ngx_stream_geoip_module\n        checkVariable(\"$geoip_city_continent_code\");   // ngx_http_geoip_module\n        checkVariable(\"$geoip_city_continent_code\");   // ngx_stream_geoip_module\n        checkVariable(\"$geoip_city_country_code\");     // ngx_http_geoip_module\n        checkVariable(\"$geoip_city_country_code\");     // ngx_stream_geoip_module\n        checkVariable(\"$geoip_city_country_code3\");    // ngx_http_geoip_module\n        checkVariable(\"$geoip_city_country_code3\");    // ngx_stream_geoip_module\n        checkVariable(\"$geoip_city_country_name\");     // ngx_http_geoip_module\n        checkVariable(\"$geoip_city_country_name\");     // ngx_stream_geoip_module\n        checkVariable(\"$geoip_country_code\");          // ngx_http_geoip_module\n        checkVariable(\"$geoip_country_code\");          // ngx_stream_geoip_module\n        checkVariable(\"$geoip_country_code3\");         // ngx_http_geoip_module\n        checkVariable(\"$geoip_country_code3\");         // ngx_stream_geoip_module\n        checkVariable(\"$geoip_country_name\");          // ngx_http_geoip_module\n        checkVariable(\"$geoip_country_name\");          // ngx_stream_geoip_module\n        checkVariable(\"$geoip_dma_code\");              // ngx_http_geoip_module\n        checkVariable(\"$geoip_dma_code\");              // ngx_stream_geoip_module\n        checkVariable(\"$geoip_latitude\");              // ngx_http_geoip_module\n        checkVariable(\"$geoip_latitude\");              // ngx_stream_geoip_module\n        checkVariable(\"$geoip_longitude\");             // ngx_http_geoip_module\n        checkVariable(\"$geoip_longitude\");             // ngx_stream_geoip_module\n        checkVariable(\"$geoip_org\");                   // ngx_http_geoip_module\n        checkVariable(\"$geoip_org\");                   // ngx_stream_geoip_module\n        checkVariable(\"$geoip_postal_code\");           // ngx_http_geoip_module\n        checkVariable(\"$geoip_postal_code\");           // ngx_stream_geoip_module\n        checkVariable(\"$geoip_region\");                // ngx_http_geoip_module\n        checkVariable(\"$geoip_region\");                // ngx_stream_geoip_module\n        checkVariable(\"$geoip_region_name\");           // ngx_http_geoip_module\n        checkVariable(\"$geoip_region_name\");           // ngx_stream_geoip_module\n\n        checkVariable(\"$gzip_ratio\");                  // ngx_http_gzip_module\n\n        checkVariable(\"$spdy\");                        // ngx_http_spdy_module\n        checkVariable(\"$spdy_request_priority\");       // ngx_http_spdy_module\n        checkVariable(\"$http2\");                       // ngx_http_v2_module\n\n        checkVariable(\"$invalid_referer\");             // ngx_http_referer_module\n\n        checkVariable(\"$jwt_claim_foobar\");            // ngx_http_auth_jwt_module\n        checkVariable(\"$jwt_header_foobar\");           // ngx_http_auth_jwt_module\n\n        checkVariable(\"$memcached_key\");               // ngx_http_memcached_module\n\n        checkVariable(\"$realip_remote_addr\");          // ngx_http_realip_module\n        checkVariable(\"$realip_remote_addr\");          // ngx_stream_realip_module\n        checkVariable(\"$realip_remote_port\");          // ngx_http_realip_module\n        checkVariable(\"$realip_remote_port\");          // ngx_stream_realip_module\n\n        // Everything that is mentioned on\n        // https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/log-format/\n        checkVariable(\"$proxy_protocol_addr\");         // remote address if proxy protocol is enabled\n        checkVariable(\"$remote_addr\");                 // remote address if proxy protocol is disabled (default)\n        checkVariable(\"$the_real_ip\");                 // the source IP address of the client\n        checkVariable(\"$remote_user\");                 // user name supplied with the Basic authentication\n        checkVariable(\"$time_local\");                  // local time in the Common Log Format\n        checkVariable(\"$request\");                     // full original request line\n        checkVariable(\"$status\");                      // response status\n        checkVariable(\"$body_bytes_sent\");             // number of bytes sent to a client, not counting the response header\n        checkVariable(\"$http_referer\");                // value of the Referer header\n        checkVariable(\"$http_user_agent\");             // value of User-Agent header\n        checkVariable(\"$request_length\");              // request length (including request line, header, and request body)\n        checkVariable(\"$request_time\");                // time elapsed since the first bytes were read from the client\n        checkVariable(\"$proxy_upstream_name\");         // name of the upstream. The format is upstream-<namespace>-<service name>-<service port>\n        checkVariable(\"$upstream_addr\");               // the IP address and port (or the path to the domain socket) of the upstream server.\n        checkVariable(\"$upstream_response_length\");    // the length of the response obtained from the upstream server\n        checkVariable(\"$upstream_response_time\");      // time spent on receiving the response from the upstream server\n        checkVariable(\"$upstream_status\");             // status code of the response obtained from the upstream server\n        checkVariable(\"$req_id\");                      // the randomly generated ID of the request\n        // Additionals\n        checkVariable(\"$namespace\");                   // namespace of the ingress\n        checkVariable(\"$ingress_name\");                // name of the ingress\n        checkVariable(\"$service_name\");                // name of the service\n        checkVariable(\"$service_port\");                // port of the service\n    }\n\n    public void checkVariable(String variableName) {\n        List<String> allPossible =\n            DissectorTester.create()\n                .verbose()\n                .withParser(new HttpdLoglineParser<>(TestRecord.class, \"# \" + variableName + \" #\"))\n                .getPossible();\n\n        allPossible.forEach(p -> assertFalse(p.startsWith(\"UNKNOWN_NGINX_VARIABLE\"), p));\n\n    }\n\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/test/java/nl/basjes/parse/httpdlog/nginxmodules/NginxUpstreamTest.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage nl.basjes.parse.httpdlog.nginxmodules;\n\nimport nl.basjes.parse.core.test.DissectorTester;\nimport nl.basjes.parse.core.test.TestRecord;\nimport nl.basjes.parse.httpdlog.HttpdLoglineParser;\nimport org.junit.jupiter.api.Test;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n// CHECKSTYLE.OFF: LineLength\nclass NginxUpstreamTest {\n\n    private static final Logger LOG = LoggerFactory.getLogger(NginxUpstreamTest.class);\n\n    private static class SingleFieldTestcase {\n        final String logformat;\n        final String logline;\n        final String fieldName;\n        final String expectedValue;\n\n        SingleFieldTestcase(String logformat, String logline, String fieldName, String expectedValue) {\n            this.logformat = logformat;\n            this.logline = logline;\n            this.fieldName = fieldName;\n            this.expectedValue = expectedValue;\n        }\n    }\n\n    @Test\n    void testBasicLogFormat() {\n        // From: http://articles.slicehost.com/2010/8/27/customizing-nginx-web-logs\n        String logFormat = \"\\\"$upstream_addr\\\" \\\"$upstream_bytes_received\\\"\";\n        String logLine = \"\\\"192.168.1.1:80, 192.168.1.2:80, unix:/tmp/sock : 192.168.10.1:80, 192.168.10.2:80\\\" \\\"1, 2, 3 : 4, 5\\\"\";\n\n        DissectorTester.create()\n            .verbose()\n            .withParser(new HttpdLoglineParser<>(TestRecord.class, logFormat))\n            .withInput(logLine)\n\n            .expect(\"UPSTREAM_ADDR_LIST:nginxmodule.upstream.addr\",\n                \"192.168.1.1:80, 192.168.1.2:80, unix:/tmp/sock : 192.168.10.1:80, 192.168.10.2:80\")\n            .expect(\"UPSTREAM_ADDR:nginxmodule.upstream.addr.0.value\",                  \"192.168.1.1:80\")\n            .expect(\"UPSTREAM_ADDR:nginxmodule.upstream.addr.0.redirected\",             \"192.168.1.1:80\")\n            .expect(\"UPSTREAM_ADDR:nginxmodule.upstream.addr.1.value\",                  \"192.168.1.2:80\")\n            .expect(\"UPSTREAM_ADDR:nginxmodule.upstream.addr.1.redirected\",             \"192.168.1.2:80\")\n            .expect(\"UPSTREAM_ADDR:nginxmodule.upstream.addr.2.value\",                  \"unix:/tmp/sock\")\n            .expect(\"UPSTREAM_ADDR:nginxmodule.upstream.addr.2.redirected\",             \"192.168.10.1:80\")\n            .expect(\"UPSTREAM_ADDR:nginxmodule.upstream.addr.3.value\",                  \"192.168.10.2:80\")\n            .expect(\"UPSTREAM_ADDR:nginxmodule.upstream.addr.3.redirected\",             \"192.168.10.2:80\")\n            .expectAbsentString(\"UPSTREAM_ADDR:nginxmodule.upstream.addr.4.value\")\n            .expectAbsentString(\"UPSTREAM_ADDR:nginxmodule.upstream.addr.4.redirected\")\n\n            .expect(\"UPSTREAM_BYTES_LIST:nginxmodule.upstream.bytes.received\",          \"1, 2, 3 : 4, 5\")\n            .expect(\"BYTES:nginxmodule.upstream.bytes.received.0.value\",                \"1\")\n            .expect(\"BYTES:nginxmodule.upstream.bytes.received.0.redirected\",           \"1\")\n            .expect(\"BYTES:nginxmodule.upstream.bytes.received.1.value\",                \"2\")\n            .expect(\"BYTES:nginxmodule.upstream.bytes.received.1.redirected\",           \"2\")\n            .expect(\"BYTES:nginxmodule.upstream.bytes.received.2.value\",                \"3\")\n            .expect(\"BYTES:nginxmodule.upstream.bytes.received.2.redirected\",           \"4\")\n            .expect(\"BYTES:nginxmodule.upstream.bytes.received.3.value\",                \"5\")\n            .expect(\"BYTES:nginxmodule.upstream.bytes.received.3.redirected\",           \"5\")\n            .expectAbsentString(\"BYTES:nginxmodule.upstream.bytes.received.4.value\")\n            .expectAbsentString(\"BYTES:nginxmodule.upstream.bytes.received.4.redirected\")\n\n\n            .checkExpectations()\n\n            .printPossible()\n            .printAllPossibleValues();\n    }\n\n    @Test\n    void testFullLine() {\n        String logFormat = \"$remote_addr - $remote_user [$time_local] \\\"$request\\\" $status $body_bytes_sent \\\"$http_referer\\\" \\\"$http_user_agent\\\" \\\"$http_x_forwarded_for\\\" $request_time $upstream_response_time $pipe\";\n\n        String logLine   = \"10.77.150.123 - - [15/Dec/2018:19:27:57 -0500] \\\"GET /25.chunk.js HTTP/1.1\\\" 200 84210 \\\"https://api.demo.com/\\\" \\\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36\\\" \\\"-\\\" 0.002 0.002 .\";\n\n        DissectorTester.create()\n            .verbose()\n            .withParser(new HttpdLoglineParser<>(TestRecord.class, logFormat))\n            .withInput(logLine)\n            .expect(\"SECOND_MILLIS:nginxmodule.upstream.response.time.0.value\",      \"0.002\")\n            .expect(\"SECOND_MILLIS:nginxmodule.upstream.response.time.0.redirected\", \"0.002\")\n            .expect(\"MICROSECONDS:nginxmodule.upstream.response.time.0.value\",      \"2000\")\n            .expect(\"MICROSECONDS:nginxmodule.upstream.response.time.0.redirected\", \"2000\")\n\n            .checkExpectations()\n\n//            .printPossible();\n            .printAllPossibleValues();\n\n    }\n\n    @Test\n    void validateAllFields() {\n        List<SingleFieldTestcase> fieldsTests = new ArrayList<>();\n\n        String addrList = \"192.168.1.1:80, 192.168.1.2:80, unix:/tmp/sock : 192.168.10.1:80, 192.168.10.2:80\";\n        final String numList = \"1, 2, 3 : 4, 5\";\n        final String timeList = \"1.001, 2.002, 3.003 : 4.004, 5.005\";\n        final String statusList = \"111, 222, 333 : 444, 555\";\n\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_addr\", addrList, \"UPSTREAM_ADDR:nginxmodule.upstream.addr.0.value\", \"192.168.1.1:80\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_addr\", addrList, \"UPSTREAM_ADDR:nginxmodule.upstream.addr.0.redirected\", \"192.168.1.1:80\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_addr\", addrList, \"UPSTREAM_ADDR:nginxmodule.upstream.addr.1.value\", \"192.168.1.2:80\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_addr\", addrList, \"UPSTREAM_ADDR:nginxmodule.upstream.addr.1.redirected\", \"192.168.1.2:80\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_addr\", addrList, \"UPSTREAM_ADDR:nginxmodule.upstream.addr.2.value\", \"unix:/tmp/sock\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_addr\", addrList, \"UPSTREAM_ADDR:nginxmodule.upstream.addr.2.redirected\", \"192.168.10.1:80\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_addr\", addrList, \"UPSTREAM_ADDR:nginxmodule.upstream.addr.3.value\", \"192.168.10.2:80\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_addr\", addrList, \"UPSTREAM_ADDR:nginxmodule.upstream.addr.3.redirected\", \"192.168.10.2:80\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_addr\", addrList, \"UPSTREAM_ADDR:nginxmodule.upstream.addr.4.value\", null));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_addr\", addrList, \"UPSTREAM_ADDR:nginxmodule.upstream.addr.4.redirected\", null));\n\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_bytes_received\", numList, \"BYTES:nginxmodule.upstream.bytes.received.0.value\", \"1\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_bytes_received\", numList, \"BYTES:nginxmodule.upstream.bytes.received.0.redirected\", \"1\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_bytes_received\", numList, \"BYTES:nginxmodule.upstream.bytes.received.1.value\", \"2\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_bytes_received\", numList, \"BYTES:nginxmodule.upstream.bytes.received.1.redirected\", \"2\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_bytes_received\", numList, \"BYTES:nginxmodule.upstream.bytes.received.2.value\", \"3\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_bytes_received\", numList, \"BYTES:nginxmodule.upstream.bytes.received.2.redirected\", \"4\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_bytes_received\", numList, \"BYTES:nginxmodule.upstream.bytes.received.3.value\", \"5\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_bytes_received\", numList, \"BYTES:nginxmodule.upstream.bytes.received.3.redirected\", \"5\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_bytes_received\", numList, \"BYTES:nginxmodule.upstream.bytes.received.4.value\", null));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_bytes_received\", numList, \"BYTES:nginxmodule.upstream.bytes.received.4.redirected\", null));\n\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_bytes_sent\", numList, \"BYTES:nginxmodule.upstream.bytes.sent.0.value\", \"1\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_bytes_sent\", numList, \"BYTES:nginxmodule.upstream.bytes.sent.0.redirected\", \"1\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_bytes_sent\", numList, \"BYTES:nginxmodule.upstream.bytes.sent.1.value\", \"2\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_bytes_sent\", numList, \"BYTES:nginxmodule.upstream.bytes.sent.1.redirected\", \"2\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_bytes_sent\", numList, \"BYTES:nginxmodule.upstream.bytes.sent.2.value\", \"3\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_bytes_sent\", numList, \"BYTES:nginxmodule.upstream.bytes.sent.2.redirected\", \"4\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_bytes_sent\", numList, \"BYTES:nginxmodule.upstream.bytes.sent.3.value\", \"5\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_bytes_sent\", numList, \"BYTES:nginxmodule.upstream.bytes.sent.3.redirected\", \"5\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_bytes_sent\", numList, \"BYTES:nginxmodule.upstream.bytes.sent.4.value\", null));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_bytes_sent\", numList, \"BYTES:nginxmodule.upstream.bytes.sent.4.redirected\", null));\n\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_cache_status\", \"STALE\", \"UPSTREAM_CACHE_STATUS:nginxmodule.upstream.cache.status\", \"STALE\"));\n\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_connect_time\", timeList, \"SECOND_MILLIS:nginxmodule.upstream.connect.time.0.value\", \"1.001\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_connect_time\", timeList, \"SECOND_MILLIS:nginxmodule.upstream.connect.time.0.redirected\", \"1.001\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_connect_time\", timeList, \"SECOND_MILLIS:nginxmodule.upstream.connect.time.1.value\", \"2.002\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_connect_time\", timeList, \"SECOND_MILLIS:nginxmodule.upstream.connect.time.1.redirected\", \"2.002\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_connect_time\", timeList, \"SECOND_MILLIS:nginxmodule.upstream.connect.time.2.value\", \"3.003\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_connect_time\", timeList, \"SECOND_MILLIS:nginxmodule.upstream.connect.time.2.redirected\", \"4.004\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_connect_time\", timeList, \"SECOND_MILLIS:nginxmodule.upstream.connect.time.3.value\", \"5.005\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_connect_time\", timeList, \"SECOND_MILLIS:nginxmodule.upstream.connect.time.3.redirected\", \"5.005\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_connect_time\", timeList, \"SECOND_MILLIS:nginxmodule.upstream.connect.time.4.value\", null));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_connect_time\", timeList, \"SECOND_MILLIS:nginxmodule.upstream.connect.time.4.redirected\", null));\n\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_cookie_mycookie\", \"MyValue\", \"HTTP.COOKIE:nginxmodule.upstream.response.cookies.mycookie\", \"MyValue\"));\n\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_header_time\", timeList, \"SECOND_MILLIS:nginxmodule.upstream.header.time.0.value\", \"1.001\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_header_time\", timeList, \"SECOND_MILLIS:nginxmodule.upstream.header.time.0.redirected\", \"1.001\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_header_time\", timeList, \"SECOND_MILLIS:nginxmodule.upstream.header.time.1.value\", \"2.002\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_header_time\", timeList, \"SECOND_MILLIS:nginxmodule.upstream.header.time.1.redirected\", \"2.002\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_header_time\", timeList, \"SECOND_MILLIS:nginxmodule.upstream.header.time.2.value\", \"3.003\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_header_time\", timeList, \"SECOND_MILLIS:nginxmodule.upstream.header.time.2.redirected\", \"4.004\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_header_time\", timeList, \"SECOND_MILLIS:nginxmodule.upstream.header.time.3.value\", \"5.005\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_header_time\", timeList, \"SECOND_MILLIS:nginxmodule.upstream.header.time.3.redirected\", \"5.005\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_header_time\", timeList, \"SECOND_MILLIS:nginxmodule.upstream.header.time.4.value\", null));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_header_time\", timeList, \"SECOND_MILLIS:nginxmodule.upstream.header.time.4.redirected\", null));\n\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_http_myheader\", \"MyValue\", \"HTTP.HEADER:nginxmodule.upstream.header.myheader\", \"MyValue\"));\n\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_queue_time\", timeList, \"SECOND_MILLIS:nginxmodule.upstream.queue.time.0.value\", \"1.001\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_queue_time\", timeList, \"SECOND_MILLIS:nginxmodule.upstream.queue.time.0.redirected\", \"1.001\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_queue_time\", timeList, \"SECOND_MILLIS:nginxmodule.upstream.queue.time.1.value\", \"2.002\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_queue_time\", timeList, \"SECOND_MILLIS:nginxmodule.upstream.queue.time.1.redirected\", \"2.002\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_queue_time\", timeList, \"SECOND_MILLIS:nginxmodule.upstream.queue.time.2.value\", \"3.003\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_queue_time\", timeList, \"SECOND_MILLIS:nginxmodule.upstream.queue.time.2.redirected\", \"4.004\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_queue_time\", timeList, \"SECOND_MILLIS:nginxmodule.upstream.queue.time.3.value\", \"5.005\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_queue_time\", timeList, \"SECOND_MILLIS:nginxmodule.upstream.queue.time.3.redirected\", \"5.005\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_queue_time\", timeList, \"SECOND_MILLIS:nginxmodule.upstream.queue.time.4.value\", null));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_queue_time\", timeList, \"SECOND_MILLIS:nginxmodule.upstream.queue.time.4.redirected\", null));\n\n\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_response_length\", numList, \"BYTES:nginxmodule.upstream.response.length.0.value\", \"1\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_response_length\", numList, \"BYTES:nginxmodule.upstream.response.length.0.redirected\", \"1\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_response_length\", numList, \"BYTES:nginxmodule.upstream.response.length.1.value\", \"2\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_response_length\", numList, \"BYTES:nginxmodule.upstream.response.length.1.redirected\", \"2\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_response_length\", numList, \"BYTES:nginxmodule.upstream.response.length.2.value\", \"3\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_response_length\", numList, \"BYTES:nginxmodule.upstream.response.length.2.redirected\", \"4\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_response_length\", numList, \"BYTES:nginxmodule.upstream.response.length.3.value\", \"5\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_response_length\", numList, \"BYTES:nginxmodule.upstream.response.length.3.redirected\", \"5\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_response_length\", numList, \"BYTES:nginxmodule.upstream.response.length.4.value\", null));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_response_length\", numList, \"BYTES:nginxmodule.upstream.response.length.4.redirected\", null));\n\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_response_time\", timeList, \"SECOND_MILLIS:nginxmodule.upstream.response.time.0.value\", \"1.001\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_response_time\", timeList, \"SECOND_MILLIS:nginxmodule.upstream.response.time.0.redirected\", \"1.001\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_response_time\", timeList, \"SECOND_MILLIS:nginxmodule.upstream.response.time.1.value\", \"2.002\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_response_time\", timeList, \"SECOND_MILLIS:nginxmodule.upstream.response.time.1.redirected\", \"2.002\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_response_time\", timeList, \"SECOND_MILLIS:nginxmodule.upstream.response.time.2.value\", \"3.003\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_response_time\", timeList, \"SECOND_MILLIS:nginxmodule.upstream.response.time.2.redirected\", \"4.004\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_response_time\", timeList, \"SECOND_MILLIS:nginxmodule.upstream.response.time.3.value\", \"5.005\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_response_time\", timeList, \"SECOND_MILLIS:nginxmodule.upstream.response.time.3.redirected\", \"5.005\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_response_time\", timeList, \"SECOND_MILLIS:nginxmodule.upstream.response.time.4.value\", null));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_response_time\", timeList, \"SECOND_MILLIS:nginxmodule.upstream.response.time.4.redirected\", null));\n\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_status\", statusList, \"UPSTREAM_STATUS:nginxmodule.upstream.status.0.value\", \"111\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_status\", statusList, \"UPSTREAM_STATUS:nginxmodule.upstream.status.0.redirected\", \"111\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_status\", statusList, \"UPSTREAM_STATUS:nginxmodule.upstream.status.1.value\", \"222\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_status\", statusList, \"UPSTREAM_STATUS:nginxmodule.upstream.status.1.redirected\", \"222\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_status\", statusList, \"UPSTREAM_STATUS:nginxmodule.upstream.status.2.value\", \"333\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_status\", statusList, \"UPSTREAM_STATUS:nginxmodule.upstream.status.2.redirected\", \"444\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_status\", statusList, \"UPSTREAM_STATUS:nginxmodule.upstream.status.3.value\", \"555\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_status\", statusList, \"UPSTREAM_STATUS:nginxmodule.upstream.status.3.redirected\", \"555\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_status\", statusList, \"UPSTREAM_STATUS:nginxmodule.upstream.status.4.value\", null));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_status\", statusList, \"UPSTREAM_STATUS:nginxmodule.upstream.status.4.redirected\", null));\n\n\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_trailer_mytrailer\", \"MyValue\", \"HTTP.TRAILER:nginxmodule.upstream.trailer.mytrailer\", \"MyValue\"));\n\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_first_byte_time\", timeList, \"SECOND_MILLIS:nginxmodule.upstream.first_byte.time.0.value\", \"1.001\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_first_byte_time\", timeList, \"SECOND_MILLIS:nginxmodule.upstream.first_byte.time.0.redirected\", \"1.001\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_first_byte_time\", timeList, \"SECOND_MILLIS:nginxmodule.upstream.first_byte.time.1.value\", \"2.002\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_first_byte_time\", timeList, \"SECOND_MILLIS:nginxmodule.upstream.first_byte.time.1.redirected\", \"2.002\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_first_byte_time\", timeList, \"SECOND_MILLIS:nginxmodule.upstream.first_byte.time.2.value\", \"3.003\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_first_byte_time\", timeList, \"SECOND_MILLIS:nginxmodule.upstream.first_byte.time.2.redirected\", \"4.004\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_first_byte_time\", timeList, \"SECOND_MILLIS:nginxmodule.upstream.first_byte.time.3.value\", \"5.005\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_first_byte_time\", timeList, \"SECOND_MILLIS:nginxmodule.upstream.first_byte.time.3.redirected\", \"5.005\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_first_byte_time\", timeList, \"SECOND_MILLIS:nginxmodule.upstream.first_byte.time.4.value\", null));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_first_byte_time\", timeList, \"SECOND_MILLIS:nginxmodule.upstream.first_byte.time.4.redirected\", null));\n\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_session_time\", timeList, \"SECOND_MILLIS:nginxmodule.upstream.session.time.0.value\", \"1.001\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_session_time\", timeList, \"SECOND_MILLIS:nginxmodule.upstream.session.time.0.redirected\", \"1.001\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_session_time\", timeList, \"SECOND_MILLIS:nginxmodule.upstream.session.time.1.value\", \"2.002\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_session_time\", timeList, \"SECOND_MILLIS:nginxmodule.upstream.session.time.1.redirected\", \"2.002\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_session_time\", timeList, \"SECOND_MILLIS:nginxmodule.upstream.session.time.2.value\", \"3.003\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_session_time\", timeList, \"SECOND_MILLIS:nginxmodule.upstream.session.time.2.redirected\", \"4.004\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_session_time\", timeList, \"SECOND_MILLIS:nginxmodule.upstream.session.time.3.value\", \"5.005\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_session_time\", timeList, \"SECOND_MILLIS:nginxmodule.upstream.session.time.3.redirected\", \"5.005\"));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_session_time\", timeList, \"SECOND_MILLIS:nginxmodule.upstream.session.time.4.value\", null));\n        fieldsTests.add(new SingleFieldTestcase(\"$upstream_session_time\", timeList, \"SECOND_MILLIS:nginxmodule.upstream.session.time.4.redirected\", null));\n\n        for (SingleFieldTestcase testCase: fieldsTests) {\n            DissectorTester tester =\n                DissectorTester.create()\n                .printSeparator()\n                .withParser(new HttpdLoglineParser<>(TestRecord.class, testCase.logformat))\n                .withInput(testCase.logline);\n            if (testCase.expectedValue == null) {\n                tester.expectAbsentString(testCase.fieldName);\n            } else {\n                tester.expect(testCase.fieldName, testCase.expectedValue);\n            }\n            tester\n                .checkExpectations();\n        }\n\n    }\n\n\n\n\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/test/java/nl/basjes/parse/httpdlog/translate/TestTranslators.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage nl.basjes.parse.httpdlog.translate;\n\nimport nl.basjes.parse.core.test.DissectorTester;\nimport nl.basjes.parse.httpdlog.dissectors.translate.ConvertCLFIntoNumber;\nimport nl.basjes.parse.httpdlog.dissectors.translate.ConvertNumberIntoCLF;\nimport org.junit.jupiter.api.Test;\n\nclass TestTranslators {\n\n    @Test\n    void testCLFToNumberMin() {\n        DissectorTester.create()\n            .withDissector(\"root\", new ConvertCLFIntoNumber(\"IN\", \"OUT\"))\n            .withInput(null) // A '-' in the input file goes into the dissector as a null value\n            .expect(\"OUT:root\", 0L)\n            .checkExpectations();\n    }\n\n    @Test\n    void testCLFToNumber0() {\n        DissectorTester.create()\n            .withDissector(\"root\", new ConvertCLFIntoNumber(\"IN\", \"OUT\"))\n            .withInput(\"0\")\n            .expect(\"OUT:root\", 0L)\n            .checkExpectations();\n    }\n\n    @Test\n    void testCLFToNumber1() {\n        DissectorTester.create()\n            .withDissector(\"root\", new ConvertCLFIntoNumber(\"IN\", \"OUT\"))\n            .withInput(\"1\")\n            .expect(\"OUT:root\", 1L)\n            .checkExpectations();\n    }\n\n    @Test\n    void testNumberToCLF0() {\n        DissectorTester.create()\n            .withDissector(\"root\", new ConvertNumberIntoCLF(\"IN\", \"OUT\"))\n            .withInput(\"0\")\n            .expect(\"OUT:root\", 0L)\n            .expect(\"OUT:root\", \"0\")\n            .checkExpectations();\n    }\n\n    @Test\n    void testNumberToCLF1() {\n        DissectorTester.create()\n            .withDissector(\"root\", new ConvertNumberIntoCLF(\"IN\", \"OUT\"))\n            .withInput(\"1\")\n            .expect(\"OUT:root\", 1L)\n            .expect(\"OUT:root\", \"1\")\n            .checkExpectations();\n    }\n\n\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-parser/src/test/resources/log4j.properties",
    "content": "#\n# Apache HTTPD & NGINX Access log parsing made easy\n# Copyright (C) 2011-2023 Niels Basjes\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# Root logger option\nlog4j.rootLogger=INFO, stdout\n#, file\nlog4j.appender.stdout=org.apache.log4j.ConsoleAppender\nlog4j.appender.stdout.Target=System.out\nlog4j.appender.stdout.threshold=INFO\nlog4j.appender.stdout.layout=org.apache.log4j.PatternLayout\nlog4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} [%-5p] %-40c{1}:%5L: %m%n\n\n## file appender\n#log4j.appender.file=org.apache.log4j.RollingFileAppender\n#log4j.appender.file.File=target/debug.log\n#log4j.appender.file.threshold=DEBUG\n#log4j.appender.file.layout=org.apache.log4j.PatternLayout\n#log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd} %d{ABSOLUTE} [%-5p] %-40c{1}:%5L: %m%n\n#log4j.appender.file.Append=false\n"
  },
  {
    "path": "httpdlog/httpdlog-serde/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n Apache HTTPD & NGINX Access log parsing made easy\n Copyright (C) 2011-2023 Niels Basjes\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n https://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n-->\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n  <modelVersion>4.0.0</modelVersion>\n\n  <parent>\n    <artifactId>httpdlog</artifactId>\n    <groupId>nl.basjes.parse.httpdlog</groupId>\n    <version>6.0.1-SNAPSHOT</version>\n  </parent>\n  <artifactId>httpdlog-serde</artifactId>\n  <name>Parser - Apache HTTPD - HCatalog SerDe</name>\n\n  <properties>\n    <!-- The Hadoop dependencies are too hard to make this check pass -->\n    <depencency-convergence.phase>none</depencency-convergence.phase>\n\n     <maven.compiler.source>21</maven.compiler.source>\n    <maven.compiler.release>21</maven.compiler.release>\n  </properties>\n\n  <dependencies>\n    <dependency>\n      <groupId>org.apache.hadoop</groupId>\n      <artifactId>hadoop-client</artifactId>\n      <version>${hadoop.version}</version>\n      <scope>provided</scope>\n      <exclusions>\n        <exclusion>\n          <artifactId>slf4j-reload4j</artifactId>\n          <groupId>org.slf4j</groupId>\n        </exclusion>\n      </exclusions>\n    </dependency>\n\n    <dependency>\n      <groupId>org.apache.hive</groupId>\n      <artifactId>hive-serde</artifactId>\n      <version>${hive.version}</version>\n      <scope>provided</scope>\n      <exclusions>\n        <exclusion>\n          <artifactId>log4j-slf4j-impl</artifactId>\n          <groupId>org.apache.logging.log4j</groupId>\n        </exclusion>\n        <exclusion>\n          <groupId>org.pentaho</groupId>\n          <artifactId>pentaho-aggdesigner-algorithm</artifactId>\n        </exclusion>\n      </exclusions>\n    </dependency>\n\n    <dependency>\n      <groupId>org.apache.hive</groupId>\n      <artifactId>hive-exec</artifactId>\n      <version>${hive.version}</version>\n      <scope>provided</scope>\n      <exclusions>\n        <exclusion>\n          <artifactId>log4j-slf4j-impl</artifactId>\n          <groupId>org.apache.logging.log4j</groupId>\n        </exclusion>\n        <exclusion>\n          <artifactId>log4j</artifactId>\n          <groupId>log4j</groupId>\n        </exclusion>\n        <exclusion>\n          <artifactId>log4j-1.2-api</artifactId>\n          <groupId>org.apache.logging.log4j</groupId>\n        </exclusion>\n        <exclusion>\n          <groupId>org.pentaho</groupId>\n          <artifactId>pentaho-aggdesigner-algorithm</artifactId>\n        </exclusion>\n      </exclusions>\n    </dependency>\n\n    <dependency>\n      <groupId>org.apache.hive</groupId>\n      <artifactId>hive-common</artifactId>\n      <version>${hive.version}</version>\n      <scope>provided</scope>\n      <exclusions>\n        <exclusion>\n          <artifactId>log4j-slf4j-impl</artifactId>\n          <groupId>org.apache.logging.log4j</groupId>\n        </exclusion>\n        <exclusion>\n          <artifactId>log4j-1.2-api</artifactId>\n          <groupId>org.apache.logging.log4j</groupId>\n        </exclusion>\n        <exclusion>\n          <groupId>org.pentaho</groupId>\n          <artifactId>pentaho-aggdesigner-algorithm</artifactId>\n        </exclusion>\n      </exclusions>\n    </dependency>\n\n    <dependency>\n      <groupId>${project.groupId}</groupId>\n      <artifactId>httpdlog-inputformat</artifactId>\n      <version>${project.version}</version>\n    </dependency>\n\n    <dependency>\n      <groupId>nl.basjes.parse</groupId>\n      <artifactId>parser-core</artifactId>\n      <version>${project.version}</version>\n      <classifier>tests</classifier>\n      <scope>test</scope>\n    </dependency>\n\n  </dependencies>\n\n  <build>\n    <plugins>\n      <plugin>\n        <groupId>org.apache.maven.plugins</groupId>\n        <artifactId>maven-toolchains-plugin</artifactId>\n        <version>3.2.0</version>\n        <executions>\n          <execution>\n            <goals>\n              <goal>select-jdk-toolchain</goal>\n            </goals>\n            <configuration>\n              <version>[21,22)</version>\n            </configuration>\n          </execution>\n        </executions>\n      </plugin>\n\n      <plugin>\n        <groupId>org.apache.maven.plugins</groupId>\n        <artifactId>maven-surefire-plugin</artifactId>\n        <configuration>\n          <argLine>\n            --add-opens=java.base/java.net=ALL-UNNAMED\n          </argLine>\n        </configuration>\n      </plugin>\n\n      <plugin>\n        <groupId>org.apache.maven.plugins</groupId>\n        <artifactId>maven-assembly-plugin</artifactId>\n        <executions>\n          <execution>\n            <id>make-super-jar</id>\n            <phase>package</phase>\n            <goals>\n              <goal>single</goal>\n            </goals>\n            <configuration>\n              <descriptors>\n                <descriptor>src/main/assembly/udf.xml</descriptor>\n              </descriptors>\n              <archive>\n                <addMavenDescriptor>true</addMavenDescriptor>\n                <manifestEntries>\n                  <Class-Path>/</Class-Path>\n                </manifestEntries>\n              </archive>\n            </configuration>\n          </execution>\n        </executions>\n      </plugin>\n      <plugin>\n        <groupId>org.jacoco</groupId>\n        <artifactId>jacoco-maven-plugin</artifactId>\n      </plugin>\n    </plugins>\n  </build>\n</project>\n"
  },
  {
    "path": "httpdlog/httpdlog-serde/src/main/assembly/udf.xml",
    "content": "<!--\n Apache HTTPD & NGINX Access log parsing made easy\n Copyright (C) 2011-2023 Niels Basjes\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n https://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n-->\n<assembly\n  xmlns=\"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2\"\n  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n  xsi:schemaLocation=\"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd\">\n  <id>udf</id>\n  <formats>\n    <format>jar</format>\n  </formats>\n  <includeBaseDirectory>false</includeBaseDirectory>\n  <dependencySets>\n    <dependencySet>\n      <useProjectArtifact>false</useProjectArtifact>\n      <unpack>true</unpack>\n      <scope>runtime</scope>\n    </dependencySet>\n    <!--\n    <dependencySet>\n      <useProjectArtifact>false</useProjectArtifact>\n      <outputDirectory>lib</outputDirectory>\n      <unpack>false</unpack>\n      <scope>runtime</scope>\n    </dependencySet>\n    <dependencySet>\n      <useProjectArtifact>false</useProjectArtifact>\n      <outputDirectory>lib</outputDirectory>\n      <unpack>false</unpack>\n      <scope>provided</scope>\n    </dependencySet>\n    -->\n  </dependencySets>\n  <fileSets>\n    <fileSet>\n      <directory>${project.build.outputDirectory}</directory>\n      <outputDirectory>${file.separator}</outputDirectory>\n    </fileSet>\n  </fileSets>\n</assembly>\n"
  },
  {
    "path": "httpdlog/httpdlog-serde/src/main/java/nl/basjes/parse/httpdlog/ApacheHttpdlogDeserializer.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage nl.basjes.parse.httpdlog;\n\nimport nl.basjes.hadoop.input.ParsedRecord;\nimport nl.basjes.parse.core.Casts;\nimport nl.basjes.parse.core.Dissector;\nimport nl.basjes.parse.core.Parser;\nimport nl.basjes.parse.core.exceptions.DissectionFailure;\nimport nl.basjes.parse.core.exceptions.InvalidDissectorException;\nimport nl.basjes.parse.core.exceptions.MissingDissectorsException;\nimport org.apache.hadoop.conf.Configuration;\nimport org.apache.hadoop.hive.serde.serdeConstants;\nimport org.apache.hadoop.hive.serde2.AbstractSerDe;\nimport org.apache.hadoop.hive.serde2.SerDeException;\nimport org.apache.hadoop.hive.serde2.SerDeStats;\nimport org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;\nimport org.apache.hadoop.hive.serde2.objectinspector.ObjectInspectorFactory;\nimport org.apache.hadoop.hive.serde2.objectinspector.StructObjectInspector;\nimport org.apache.hadoop.hive.serde2.typeinfo.TypeInfo;\nimport org.apache.hadoop.hive.serde2.typeinfo.TypeInfoUtils;\nimport org.apache.hadoop.io.Text;\nimport org.apache.hadoop.io.Writable;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.InvocationTargetException;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Properties;\nimport java.util.Set;\n\nimport static nl.basjes.parse.core.Casts.DOUBLE;\nimport static nl.basjes.parse.core.Casts.LONG;\nimport static nl.basjes.parse.core.Casts.STRING;\nimport static org.apache.hadoop.hive.serde.serdeConstants.BIGINT_TYPE_NAME;\nimport static org.apache.hadoop.hive.serde.serdeConstants.DOUBLE_TYPE_NAME;\nimport static org.apache.hadoop.hive.serde.serdeConstants.STRING_TYPE_NAME;\n\n/**\n * Hive SerDe for accessing Apache Access log files.\n * An example DDL statement\n * would be:\n * <pre>\n *\n * ADD JAR target/httpdlog-serde-1.7-SNAPSHOT-job.jar;\n * CREATE EXTERNAL TABLE nbasjes.clicks (\n *       ip           STRING\n *      ,timestamp    BIGINT\n *      ,useragent    STRING\n *      ,referrer     STRING\n *      ,bui          STRING\n *      ,screenHeight BIGINT\n *      ,screenWidth  BIGINT\n *      )\n * ROW FORMAT SERDE 'nl.basjes.parse.apachehttpdlog.ApacheHttpdlogDeserializer'\n * WITH SERDEPROPERTIES (\n *       \"logformat\"       = \"%h %l %u %t \\\"%r\\\" %&gt;s %b \\\"%{Referer}i\\\" \\\"%{User-Agent}i\\\" \\\"%{Cookie}i\\\" %T %V\"\n *      ,\"map:request.firstline.uri.query.g\" = \"HTTP.URI\"\n *      ,\"map:request.firstline.uri.query.r\" = \"HTTP.URI\"\n *\n *      ,\"field:timestamp\" = \"TIME.EPOCH:request.receive.time.epoch\"\n *      ,\"field:ip\"        = \"IP:connection.client.host\"\n *      ,\"field:useragent\" = \"HTTP.USERAGENT:request.user-agent\"\n *\n *      ,\"field:referrer\"  = \"STRING:request.firstline.uri.query.g.query.referrer\"\n *      ,\"field:bui\"       = \"HTTP.COOKIE:request.cookies.bui\"\n *\n *      ,\"load:nl.basjes.parse.httpdlog.dissectors.ScreenResolutionDissector\" = \"x\"\n *      ,\"map:request.firstline.uri.query.s\" = \"SCREENRESOLUTION\"\n *      ,\"field:screenHeight\" = \"SCREENHEIGHT:request.firstline.uri.query.s.height\"\n *      ,\"field:screenWidth\"  = \"SCREENWIDTH:request.firstline.uri.query.s.width\"\n *      )\n * STORED AS TEXTFILE\n * LOCATION \"/user/nbasjes/clicks\";\n * </pre>\n*/\n\n//@SerDeSpec(schemaProps = {\n//    serdeConstants.LIST_COLUMNS, serdeConstants.LIST_COLUMN_TYPES,\n//    RegexSerDe.INPUT_REGEX, RegexSerDe.OUTPUT_FORMAT_STRING,\n//    RegexSerDe.INPUT_REGEX_CASE_SENSITIVE\n//})\npublic class ApacheHttpdlogDeserializer extends AbstractSerDe {\n    private static final Logger      LOG = LoggerFactory.getLogger(ApacheHttpdlogDeserializer.class);\n    private static final String      FIELD = \"field:\";\n\n    private static final String      MAP_FIELD = \"map:\";\n    private static final int         MAP_FIELD_LENGTH = MAP_FIELD.length();\n    private static final String      LOAD_DISSECTOR = \"load:\";\n    private static final int         LOAD_DISSECTOR_LENGTH = LOAD_DISSECTOR.length();\n\n    private StructObjectInspector    rowOI;\n    private ArrayList<Object>        row;\n\n    private Parser<ParsedRecord>     parser;\n    private ParsedRecord             currentValue;\n\n\n    // We do not want the parsing to fail immediately when we hit a single 'bad' line.\n    // So we count the good and bad lines.\n    // If we see more than 1% bad lines we abort (after we have seen 1000 lines)\n    private static final long    MINIMAL_FAIL_LINES      = 1000;\n    private static final int     MINIMAL_FAIL_PERCENTAGE =    1;\n    private long    linesInput  = 0;\n    private long    linesBad    = 0;\n\n    static class ColumnToGetterMapping {\n        private int    index;\n        private Casts  casts;\n        private String fieldValue;\n    }\n\n    private final List<ColumnToGetterMapping> columnToGetterMappings = new ArrayList<>();\n\n    @Override\n    public void initialize(Configuration conf, Properties tableProperties, Properties partitionProperties) throws SerDeException {\n\n        boolean usable = true;\n        linesInput = 0;\n        linesBad   = 0;\n\n        String logformat = tableProperties.getProperty(\"logformat\");\n\n        Map<String, Set<String>> typeRemappings = new HashMap<>();\n        List<Dissector> additionalDissectors = new ArrayList<>();\n\n        for (Map.Entry<Object, Object> property: tableProperties.entrySet()){\n            String key = (String)property.getKey();\n\n            if (key.startsWith(MAP_FIELD)) {\n                String mapField = key.substring(MAP_FIELD_LENGTH);\n                String mapType  = (String)property.getValue();\n\n                Set<String> remapping = typeRemappings.computeIfAbsent(mapField, k -> new HashSet<>());\n                remapping.add(mapType);\n                LOG.info(\"Add mapping for field \\\"{}\\\" to type \\\"{}\\\"\", mapField, mapType);\n                continue;\n            }\n\n            if (key.startsWith(LOAD_DISSECTOR)) {\n                String dissectorClassName = key.substring(LOAD_DISSECTOR_LENGTH);\n                String dissectorParam = (String)property.getValue();\n\n                try {\n                    Class<?> clazz = Class.forName(dissectorClassName);\n                    Constructor<?> constructor = clazz.getConstructor();\n                    Dissector instance = (Dissector) constructor.newInstance();\n                    if (!instance.initializeFromSettingsParameter(dissectorParam)) {\n                        throw new SerDeException(\"Initialization failed of dissector instance of class \" + dissectorClassName);\n                    }\n                    additionalDissectors.add(instance);\n                } catch (ClassNotFoundException e) {\n                    throw new SerDeException(\"Found load with bad specification: No such class:\" + dissectorClassName, e);\n                } catch (NoSuchMethodException e) {\n                    throw new SerDeException(\"Found load with bad specification: Class does not have the required constructor\", e);\n                } catch (InvocationTargetException e) {\n                    throw new SerDeException(\"Got an InvocationTargetException\", e);\n                } catch (InstantiationException e) {\n                    throw new SerDeException(\"Got an InstantiationException\", e);\n                } catch (IllegalAccessException e) {\n                    throw new SerDeException(\"Found load with bad specification: Required constructor is not public\", e);\n                }\n                LOG.debug(\"Loaded additional dissector: {}(\\\"{}\\\")\", dissectorClassName, dissectorParam);\n            }\n        }\n\n        currentValue = new ParsedRecord();\n\n\n//        List<String>            fieldList;\n        int                     numColumns;\n\n        String columnNameProperty  = tableProperties.getProperty(serdeConstants.LIST_COLUMNS);\n        String columnTypeProperty  = tableProperties.getProperty(serdeConstants.LIST_COLUMN_TYPES);\n        List<String> columnNames   = Arrays.asList(columnNameProperty.split(\",\"));\n        List<TypeInfo> columnTypes = TypeInfoUtils.getTypeInfosFromTypeString(columnTypeProperty);\n        assert columnNames.size() == columnTypes.size();\n        numColumns = columnNames.size();\n\n        parser = new HttpdLoglineParser<>(ParsedRecord.class, logformat);\n        parser.setTypeRemappings(typeRemappings)\n              .addDissectors(additionalDissectors);\n\n        List<ObjectInspector> columnOIs = new ArrayList<>(columnNames.size());\n\n        try {\n            for (int columnNr = 0; columnNr < numColumns; columnNr++) {\n                columnOIs.add(TypeInfoUtils.getStandardJavaObjectInspectorFromTypeInfo(columnTypes.get(columnNr)));\n                String columnName = columnNames.get(columnNr);\n                TypeInfo columnType = columnTypes.get(columnNr);\n\n                String fieldValue = tableProperties.getProperty(FIELD + columnName);\n\n                if (fieldValue == null) {\n                    LOG.error(\"MUST have Field value for column \\\"{}\\\".\", columnName);\n                    usable = false;\n                    continue;\n                }\n\n                ColumnToGetterMapping ctgm = new ColumnToGetterMapping();\n                ctgm.index      = columnNr;\n                ctgm.fieldValue = fieldValue;\n\n                List<String> singleFieldValue= new ArrayList<>();\n                singleFieldValue.add(fieldValue);\n                switch (columnType.getTypeName()) {\n                    case STRING_TYPE_NAME:\n                        ctgm.casts = STRING;\n                        parser.addParseTarget(ParsedRecord.class.getMethod(\"set\", String.class, String.class), singleFieldValue);\n                        break;\n                    case BIGINT_TYPE_NAME:\n                        ctgm.casts = LONG;\n                        parser.addParseTarget(ParsedRecord.class.getMethod(\"set\", String.class, Long.class), singleFieldValue);\n                        break;\n                    case DOUBLE_TYPE_NAME:\n                        ctgm.casts = DOUBLE;\n                        parser.addParseTarget(ParsedRecord.class.getMethod(\"set\", String.class, Double.class), singleFieldValue);\n                        break;\n                    default:\n                        LOG.error(\"Requested column type {} is not supported at this time.\", columnType.getTypeName());\n                        usable = false;\n                        break;\n                }\n                columnToGetterMappings.add(ctgm);\n            }\n        } catch (NoSuchMethodException\n                |SecurityException e) {\n            throw new SerDeException(\"(Should not occur) Caught exception: {}\", e);\n        }\n\n        // StandardStruct uses ArrayList to store the row.\n        rowOI = ObjectInspectorFactory.getStandardStructObjectInspector(columnNames, columnOIs);\n\n        // Constructing the row object, etc, which will be reused for all rows.\n        row = new ArrayList<>(numColumns);\n        for (int c = 0; c < numColumns; c++) {\n            row.add(null);\n        }\n\n        if (!usable) {\n            throw new SerDeException(\"Fatal config error. Check the logged error messages why.\");\n        }\n\n    }\n\n    @Override\n    public ObjectInspector getObjectInspector() {\n        return rowOI;\n    }\n\n    @Override\n    public Object deserialize(Writable writable) throws SerDeException {\n        if (!(writable instanceof Text)) {\n            throw new SerDeException(\"The input MUST be a Text line.\");\n        }\n\n        linesInput++;\n\n        try {\n            currentValue.clear();\n            parser.parse(currentValue, writable.toString());\n        } catch (DissectionFailure dissectionFailure) {\n            linesBad++;\n            if (linesInput >= MINIMAL_FAIL_LINES) {\n                if (100* linesBad > MINIMAL_FAIL_PERCENTAGE * linesInput){\n                    throw new SerDeException(\"To many bad lines: \" + linesBad + \" of \" + linesInput + \" are bad.\");\n                }\n            }\n            return null; // Just return that this line is nothing.\n        } catch (InvalidDissectorException |MissingDissectorsException e) {\n            throw new SerDeException(\"Cannot continue; Fix the Dissectors before retrying\", e);\n        }\n\n        for (ColumnToGetterMapping ctgm: columnToGetterMappings) {\n            switch(ctgm.casts) {\n                case STRING:\n                    String currentValueString = currentValue.getString(ctgm.fieldValue);\n                    row.set(ctgm.index, currentValueString);\n                    break;\n                case LONG:\n                    Long currentValueLong = currentValue.getLong(ctgm.fieldValue);\n                    row.set(ctgm.index, currentValueLong);\n                    break;\n                case DOUBLE:\n                    Double currentValueDouble = currentValue.getDouble(ctgm.fieldValue);\n                    row.set(ctgm.index, currentValueDouble);\n                    break;\n                default:\n                    // Do nothing\n            }\n        }\n\n        return row;\n    }\n\n    @Override\n    public Class<? extends Writable> getSerializedClass() {\n        return null; // This is NOT a Serializer, ONLY a Deserializer!\n    }\n\n    @Override\n    public SerDeStats getSerDeStats() {\n        return new SerDeStats();\n    }\n\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-serde/src/test/java/nl/basjes/parse/httpdlog/TestAllDissectorTypes.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage nl.basjes.parse.httpdlog;\n\nimport org.apache.hadoop.conf.Configuration;\nimport org.apache.hadoop.hive.serde.serdeConstants;\nimport org.apache.hadoop.hive.serde2.AbstractSerDe;\nimport org.apache.hadoop.hive.serde2.SerDeException;\nimport org.apache.hadoop.io.Text;\nimport org.junit.jupiter.api.Test;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.util.List;\nimport java.util.Properties;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\npublic class TestAllDissectorTypes {\n\n    private static final Logger LOG = LoggerFactory.getLogger(TestAllDissectorTypes.class);\n\n    @Test\n    void testAllDissectorOutputTypes() throws Throwable {\n        // Create the SerDe\n        AbstractSerDe serDe = getTestSerDe();\n\n        // Data\n        Text t = new Text(\"Doesn't matter\");\n\n        // Deserialize\n        Object row = serDe.deserialize(t);\n//        ObjectInspector rowOI = serDe.getObjectInspector();\n\n        assertTrue(row instanceof List);\n\n        @SuppressWarnings(\"unchecked\")\n        List<Object> rowArray = (List<Object>)row;\n        LOG.debug(\"Deserialized row: {}\", row);\n\n        int index = -1;\n        assertEquals(\"42\",            rowArray.get(++index)); // any_string\n        assertEquals(42L,             rowArray.get(++index)); // any_long\n        assertEquals(42D,             rowArray.get(++index)); // any_double\n\n        assertEquals(\"FortyTwo\",      rowArray.get(++index)); // string_string\n        assertEquals(null,            rowArray.get(++index)); // string_long\n        assertEquals(null,            rowArray.get(++index)); // string_double\n\n        assertEquals(\"42\",            rowArray.get(++index)); // int_string\n        assertEquals(42L,             rowArray.get(++index)); // int_long\n        assertEquals(null,            rowArray.get(++index)); // int_double\n\n        assertEquals(\"42\",            rowArray.get(++index)); // long_string\n        assertEquals(42L,             rowArray.get(++index)); // long_long\n        assertEquals(null,            rowArray.get(++index)); // long_double\n\n        assertEquals(\"42.0\",          rowArray.get(++index)); // float_string\n        assertEquals(null,            rowArray.get(++index)); // float_long\n        assertEquals(42D,             rowArray.get(++index)); // float_double\n\n        assertEquals(\"42.0\",          rowArray.get(++index)); // double_string\n        assertEquals(null,            rowArray.get(++index)); // double_long\n        assertEquals(42D,             rowArray.get(++index)); // double_double\n    }\n\n    private AbstractSerDe getTestSerDe() throws SerDeException {\n        // Create the SerDe\n        Properties schema = new Properties();\n        schema.setProperty(serdeConstants.LIST_COLUMNS,\n            \"any_string,\" +\n            \"any_long,\" +\n            \"any_double,\" +\n            \"string_string,\" +\n            \"string_long,\" +\n            \"string_double,\" +\n            \"int_string,\" +\n            \"int_long,\" +\n            \"int_double,\" +\n            \"long_string,\" +\n            \"long_long,\" +\n            \"long_double,\" +\n            \"float_string,\" +\n            \"float_long,\" +\n            \"float_double,\" +\n            \"double_string,\" +\n            \"double_long,\" +\n            \"double_double\"\n        );\n\n        schema.setProperty(serdeConstants.LIST_COLUMN_TYPES,\n            \"string,\" +\n            \"bigint,\" +\n            \"double,\" +\n            \"string,\" +\n            \"bigint,\" +\n            \"double,\" +\n            \"string,\" +\n            \"bigint,\" +\n            \"double,\" +\n            \"string,\" +\n            \"bigint,\" +\n            \"double,\" +\n            \"string,\" +\n            \"bigint,\" +\n            \"double,\" +\n            \"string,\" +\n            \"bigint,\" +\n            \"double,\"\n        );\n\n        schema.setProperty(\"logformat\",           \"%t\");\n        schema.setProperty(\"load:nl.basjes.parse.core.test.NormalValuesDissector\", HttpdLogFormatDissector.INPUT_TYPE);\n\n        schema.setProperty(\"field:any_string\",     \"ANY:any\");\n        schema.setProperty(\"field:any_long\",       \"ANY:any\");\n        schema.setProperty(\"field:any_double\",     \"ANY:any\");\n        schema.setProperty(\"field:string_string\",  \"STRING:string\");\n        schema.setProperty(\"field:string_long\",    \"STRING:string\");\n        schema.setProperty(\"field:string_double\",  \"STRING:string\");\n        schema.setProperty(\"field:int_string\",     \"INT:int\");\n        schema.setProperty(\"field:int_long\",       \"INT:int\");\n        schema.setProperty(\"field:int_double\",     \"INT:int\");\n        schema.setProperty(\"field:long_string\",    \"LONG:long\");\n        schema.setProperty(\"field:long_long\",      \"LONG:long\");\n        schema.setProperty(\"field:long_double\",    \"LONG:long\");\n        schema.setProperty(\"field:float_string\",   \"FLOAT:float\");\n        schema.setProperty(\"field:float_long\",     \"FLOAT:float\");\n        schema.setProperty(\"field:float_double\",   \"FLOAT:float\");\n        schema.setProperty(\"field:double_string\",  \"DOUBLE:double\");\n        schema.setProperty(\"field:double_long\",    \"DOUBLE:double\");\n        schema.setProperty(\"field:double_double\",  \"DOUBLE:double\");\n\n        AbstractSerDe serDe = new ApacheHttpdlogDeserializer();\n        serDe.initialize(new Configuration(), schema, null);\n        return serDe;\n    }\n\n}\n\n"
  },
  {
    "path": "httpdlog/httpdlog-serde/src/test/java/nl/basjes/parse/httpdlog/TestApacheHttpdlogDeserializer.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage nl.basjes.parse.httpdlog;\n\nimport org.apache.hadoop.conf.Configuration;\nimport org.apache.hadoop.hive.serde.serdeConstants;\nimport org.apache.hadoop.hive.serde2.AbstractSerDe;\nimport org.apache.hadoop.hive.serde2.SerDeException;\nimport org.apache.hadoop.io.Text;\nimport org.junit.jupiter.api.Test;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.util.List;\nimport java.util.Properties;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\nimport static org.junit.jupiter.api.Assertions.assertNull;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.junit.jupiter.api.Assertions.fail;\n\nclass TestApacheHttpdlogDeserializer {\n\n    private static final Logger LOG = LoggerFactory.getLogger(TestApacheHttpdlogDeserializer.class);\n\n    private final String logformat = \"%h %a %A %l %u %t \\\"%r\\\" \" +\n        \"%>s %b %p \\\"%q\\\" \\\"%{Referer}i\\\" %D \\\"%{User-agent}i\\\" \" +\n        \"\\\"%{Cookie}i\\\" \" +\n        \"\\\"%{Set-Cookie}o\\\" \" +\n        \"\\\"%{If-None-Match}i\\\" \\\"%{Etag}o\\\"\";\n\n    private final String testLogLine =\n        \"127.0.0.1 127.0.0.1 127.0.0.1 - - [24/Oct/2012:23:00:44 +0200] \\\"GET /index.php?s=800x600 HTTP/1.1\\\" \" +\n        \"200 - 80 \\\"\\\" \\\"-\\\" 80991 \\\"Mozilla/5.0 (X11; Linux i686 on x86_64; rv:11.0) Gecko/20100101 Firefox/11.0\\\" \" +\n        \"\\\"jquery-ui-theme=Eggplant; Apache=127.0.0.1.1351111543699529\\\" \" +\n        \"\\\"\" +\n        \"NBA-1=1234, \" +\n        \"NBA-2=1234; expires=Wed, 01-Jan-2020 00:00:10 GMT, \" +\n        \"NBA-3=1234; expires=Wed, 01-Jan-2020 00:00:10 GMT; path=/, \" +\n        \"NBA-4=1234; expires=Wed, 01-Jan-2020 00:00:10 GMT; path=/; domain=.basj.es\" +\n        \"\\\" \\\"-\\\" \\\"-\\\"\";\n\n    @Test\n    void testBasicParse() throws Throwable {\n        // Create the SerDe\n        AbstractSerDe serDe = getTestSerDe();\n\n        // Data\n        Text t = new Text(testLogLine);\n\n        // Deserialize\n        Object row = serDe.deserialize(t);\n\n        if (!(row instanceof List)) {\n            fail(\"row must be instanceof List<>\");\n        }\n        List<?> rowArray = (List<?>)row;\n        LOG.debug(\"Deserialized row: {}\", row);\n        assertEquals(\"127.0.0.1\",     rowArray.get(0));\n        assertEquals(1351112444000L,  rowArray.get(1));\n        assertEquals(\"Mozilla/5.0 (X11; Linux i686 on x86_64; rv:11.0) Gecko/20100101 Firefox/11.0\", rowArray.get(2));\n        assertEquals(800L,            rowArray.get(3));\n        assertEquals(600L,            rowArray.get(4));\n    }\n\n    @Test\n    void testHighFailRatio1() throws Throwable {\n        AbstractSerDe serDe = getTestSerDe();\n\n        // Data\n        Text goodLine = new Text(testLogLine);\n        Text badLine = new Text(\"A really bad line\");\n        Object row;\n\n        // Deserialize good line\n        row = serDe.deserialize(goodLine);\n        assertNotNull(row);\n\n        // Deserialize bad line\n        row = serDe.deserialize(badLine);\n        assertNull(row);\n\n        for (int i = 0; i < 999; i++) {\n            // Deserialize good line\n            row = serDe.deserialize(goodLine);\n            assertNotNull(row);\n        }\n        assertThrows(SerDeException.class, () -> {\n            Object extraRow;\n            for (int i = 0; i < 99; i++) {\n                // Deserialize bad line\n                extraRow = serDe.deserialize(badLine);\n                assertNull(extraRow);\n            }\n        });\n    }\n\n    private AbstractSerDe getTestSerDe() throws SerDeException {\n        // Create the SerDe\n        Properties schema = new Properties();\n        schema.setProperty(serdeConstants.LIST_COLUMNS,\n            \"ip,timestamp,useragent,screenWidth,screenHeight\");\n        schema.setProperty(serdeConstants.LIST_COLUMN_TYPES,\n            \"string,bigint,string,bigint,bigint\");\n\n        schema.setProperty(\"logformat\",           logformat);\n        schema.setProperty(\"field:timestamp\",     \"TIME.EPOCH:request.receive.time.epoch\");\n        schema.setProperty(\"field:ip\",            \"IP:connection.client.host\");\n        schema.setProperty(\"field:useragent\",     \"HTTP.USERAGENT:request.user-agent\");\n        schema.setProperty(\"load:nl.basjes.parse.httpdlog.dissectors.ScreenResolutionDissector\", \"x\");\n        schema.setProperty(\"map:request.firstline.uri.query.s\", \"SCREENRESOLUTION\");\n        schema.setProperty(\"field:screenWidth\",   \"SCREENWIDTH:request.firstline.uri.query.s.width\");\n        schema.setProperty(\"field:screenHeight\",  \"SCREENHEIGHT:request.firstline.uri.query.s.height\");\n\n        AbstractSerDe serDe = new ApacheHttpdlogDeserializer();\n        serDe.initialize(new Configuration(), schema, null);\n        return serDe;\n    }\n\n\n}\n"
  },
  {
    "path": "httpdlog/httpdlog-serde/src/test/resources/log4j.properties",
    "content": "#\n# Apache HTTPD & NGINX Access log parsing made easy\n# Copyright (C) 2011-2023 Niels Basjes\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# Root logger option\nlog4j.rootLogger=INFO, stdout\n#, file\nlog4j.appender.stdout=org.apache.log4j.ConsoleAppender\nlog4j.appender.stdout.Target=System.out\nlog4j.appender.stdout.threshold=INFO\nlog4j.appender.stdout.layout=org.apache.log4j.PatternLayout\nlog4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} [%-5p] %-40c{1}:%5L: %m%n\n\n## file appender\n#log4j.appender.file=org.apache.log4j.RollingFileAppender\n#log4j.appender.file.File=target/debug.log\n#log4j.appender.file.threshold=DEBUG\n#log4j.appender.file.layout=org.apache.log4j.PatternLayout\n#log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd} %d{ABSOLUTE} [%-5p] %-40c{1}:%5L: %m%n\n#log4j.appender.file.Append=false\n"
  },
  {
    "path": "httpdlog/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n Apache HTTPD & NGINX Access log parsing made easy\n Copyright (C) 2011-2023 Niels Basjes\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n https://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n-->\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n  <modelVersion>4.0.0</modelVersion>\n  <parent>\n    <artifactId>parser-parent</artifactId>\n    <groupId>nl.basjes.parse</groupId>\n    <version>6.0.1-SNAPSHOT</version>\n  </parent>\n\n  <groupId>nl.basjes.parse.httpdlog</groupId>\n  <artifactId>httpdlog</artifactId>\n  <packaging>pom</packaging>\n  <name>Parser - Apache HTTPD -</name>\n\n  <modules>\n    <module>httpdlog-parser</module>\n    <module>httpdlog-inputformat</module>\n    <module>httpdlog-serde</module>\n  </modules>\n\n  <build>\n    <plugins>\n      <plugin>\n        <groupId>org.apache.maven.plugins</groupId>\n        <artifactId>maven-checkstyle-plugin</artifactId>\n      </plugin>\n    </plugins>\n  </build>\n\n</project>\n"
  },
  {
    "path": "parser-core/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n Apache HTTPD & NGINX Access log parsing made easy\n Copyright (C) 2011-2023 Niels Basjes\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n https://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n-->\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n  <modelVersion>4.0.0</modelVersion>\n  <parent>\n    <artifactId>parser-parent</artifactId>\n    <groupId>nl.basjes.parse</groupId>\n    <version>6.0.1-SNAPSHOT</version>\n  </parent>\n  <artifactId>parser-core</artifactId>\n  <name>Parser - Core</name>\n\n  <build>\n    <plugins>\n      <plugin>\n        <groupId>org.apache.maven.plugins</groupId>\n        <artifactId>maven-checkstyle-plugin</artifactId>\n      </plugin>\n      <plugin>\n        <groupId>org.jacoco</groupId>\n        <artifactId>jacoco-maven-plugin</artifactId>\n        <configuration>\n          <excludes>\n            <exclude>nl/basjes/parse/core/Field.class</exclude>\n            <exclude>nl/basjes/parse/core/exceptions/*.class</exclude>\n          </excludes>\n        </configuration>\n      </plugin>\n\n      <plugin>\n        <groupId>org.apache.maven.plugins</groupId>\n        <artifactId>maven-jar-plugin</artifactId>\n        <executions>\n          <execution>\n            <goals>\n              <goal>test-jar</goal>\n            </goals>\n          </execution>\n        </executions>\n      </plugin>\n    </plugins>\n  </build>\n\n</project>\n"
  },
  {
    "path": "parser-core/src/main/assembly/job.xml",
    "content": "<!--\n Apache HTTPD & NGINX Access log parsing made easy\n Copyright (C) 2011-2023 Niels Basjes\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n https://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n-->\n<assembly\n  xmlns=\"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2\"\n  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n  xsi:schemaLocation=\"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd\">\n  <id>job</id>\n  <formats>\n    <format>jar</format>\n  </formats>\n  <includeBaseDirectory>false</includeBaseDirectory>\n  <dependencySets>\n    <dependencySet>\n      <useProjectArtifact>false</useProjectArtifact>\n      <outputDirectory>lib</outputDirectory>\n      <unpack>false</unpack>\n      <scope>compile</scope>\n    </dependencySet>\n    <!--\n    <dependencySet>\n      <useProjectArtifact>false</useProjectArtifact>\n      <outputDirectory>lib</outputDirectory>\n      <unpack>false</unpack>\n      <scope>provided</scope>\n    </dependencySet>\n    -->\n  </dependencySets>\n  <fileSets>\n    <fileSet>\n      <directory>${project.build.outputDirectory}</directory>\n      <outputDirectory>${file.separator}</outputDirectory>\n    </fileSet>\n  </fileSets>\n</assembly>\n"
  },
  {
    "path": "parser-core/src/main/java/nl/basjes/parse/core/Casts.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage nl.basjes.parse.core;\n\nimport java.util.EnumSet;\n\npublic enum Casts {\n    STRING, LONG, DOUBLE;\n    public static final EnumSet<Casts> NO_CASTS         = EnumSet.noneOf(Casts.class);\n    public static final EnumSet<Casts> STRING_ONLY      = EnumSet.of(STRING);\n    public static final EnumSet<Casts> LONG_ONLY        = EnumSet.of(LONG);\n    public static final EnumSet<Casts> DOUBLE_ONLY      = EnumSet.of(DOUBLE);\n    public static final EnumSet<Casts> STRING_OR_LONG   = EnumSet.of(STRING, LONG);\n    public static final EnumSet<Casts> STRING_OR_DOUBLE = EnumSet.of(STRING, DOUBLE);\n    public static final EnumSet<Casts> STRING_OR_LONG_OR_DOUBLE = EnumSet.of(STRING, LONG, DOUBLE);\n}\n"
  },
  {
    "path": "parser-core/src/main/java/nl/basjes/parse/core/Dissector.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.core;\n\nimport nl.basjes.parse.core.exceptions.DissectionFailure;\nimport nl.basjes.parse.core.exceptions.InvalidDissectorException;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.io.Serializable;\nimport java.lang.reflect.Constructor;\nimport java.util.EnumSet;\nimport java.util.List;\n\n/**\n * <p>A Dissector is a class capable of chopping a String into multiple values of specific types.</p>\n * <p>The lifecycle:</p>\n * <p><b>Parser setup</b></p>\n * <ol>\n * <li>First instance is constructed</li>\n * <li>First instance is added to the {@link nl.basjes.parse.core.Parser} using\n * {@link nl.basjes.parse.core.Parser#addDissector(Dissector)} </li>\n * <li>The {@link nl.basjes.parse.core.Parser} calls {@link #getInputType()} and {@link #getPossibleOutput()} to know\n * what this dissector can deliver.</li>\n * </ol>\n * <p>The parser now constructs a tree based on the available {@link Dissector}s\n * and what was requested.</p>\n * <p><b>Dissectors setup</b></p>\n * <p>For each node in the tree a new instance of the required dissector is created by calling {@link #getNewInstance()}\n * which calls {@link #initializeNewInstance(Dissector)}. Note that only {@link Dissector}s\n * that are actually needed will be in the parse tree.</p>\n * <p>For each of those instances in the tree:</p>\n * <ol>\n * <li>For each of the actually needed input+output combinations {@link #prepareForDissect(String, String)} is called.\n * This can be used to avoid needless CPU cycles during the actual run.</li>\n * <li>As a final step a call to {@link #prepareForRun()} is done as an indication that all preparation input has been\n * provided. A Dissector can use this to finalize the runtime data structures so doing the actual dissecting faster.</li>\n * </ol>\n * <p><b>Dissecting</b></p>\n * <ul>\n * <li>During a run the instance will be called with {@link #dissect} many times.</li>\n * <li>In the {@link #dissect(Parsable, String)} the actual value to be worked on must be retrieved using\n * {@link nl.basjes.parse.core.Parsable#getParsableField(String, String)}</li>\n * <li>The result(s) of the dissection must be put back using\n * {@link nl.basjes.parse.core.Parsable#addDissection(String, String, String, String)}</li>\n * </ul>\n */\npublic abstract class Dissector implements Serializable {\n\n    private static final Logger LOG = LoggerFactory.getLogger(Dissector.class);\n\n    // --------------------------------------------\n\n    /**\n     * If a Dissector is loaded through an external language then this is the\n     * method that is called to set all the parameters.\n     * There is exactly one String as input so it is up to the specific\n     * Dissector implementation to parse and handle this input.\n     * @return true if everything went right. false otherwise.\n     */\n    public boolean initializeFromSettingsParameter(String settings) {\n        // Default behaviour is to do nothing.\n        return true;\n    }\n\n    // --------------------------------------------\n\n    /**\n     * This method must dissect the provided field from the parsable into 'smaller' pieces.\n     */\n    public abstract void dissect(Parsable<?> parsable, String inputname)\n        throws DissectionFailure;\n\n    // --------------------------------------------\n\n    /**\n     * @return The required typename of the input\n     */\n    public abstract String getInputType();\n\n    // --------------------------------------------\n\n    /**\n     * What are all possible outputs that can be provided.\n     * @return List of \"type:name\" values that indicates all the possible outputs. Never a null!\n     */\n    public abstract List<String> getPossibleOutput();\n\n    // --------------------------------------------\n\n    /**\n     * This tells the dissector that it should prepare that we will call it soon\n     * with 'inputname' and expect to get 'inputname.outputname' because\n     * inputname is of the type returned by getInputType and outputname\n     * was part of the answer from getPossibleOutput.\n     * This can be used by the dissector implementation to optimize the internal parsing\n     * algorithms and lookup tables and such.\n     * The dissector must return the types to which this value can be mapped later on during the run.\n     * @return The EnumSet of all allowed casts. Returns an empty EnumSet if nothing is allowed. Never a null !\n     */\n    public abstract EnumSet<Casts> prepareForDissect(String inputname, String outputname);\n\n    // --------------------------------------------\n\n    /**\n     * The framework will tell the dissector that it should get ready to run.\n     * I.e. finalize the bootstrapping.\n     */\n    public void prepareForRun() throws InvalidDissectorException {\n        // Default behaviour is do nothing.\n    }\n\n    // --------------------------------------------\n\n    /**\n     * Create an additional instance of this dissector.\n     * This is needed because in the parse tree we may need the same dissector multiple times.\n     * In order to optimize per node we need separate instances.\n     * @return New instance of this Dissector\n     */\n    public Dissector getNewInstance() throws InvalidDissectorException {\n        try {\n            Constructor<? extends Dissector> co = this.getClass().getConstructor();\n            Dissector newInstance = co.newInstance();\n            initializeNewInstance(newInstance);\n            return newInstance;\n        } catch (Exception e) {\n            String error = \"Unable to create instance of \" + this.getClass().getCanonicalName() + \": \" +\n                e.getClass().getSimpleName() + \": \" + e.getMessage();\n            LOG.error(\"{}\",  error);\n            throw new InvalidDissectorException(error, e);\n        }\n    }\n\n    public String extractFieldName(final String inputname, final String outputname){\n        String fieldName = outputname;\n\n        if (inputname.equals(outputname)) {\n            return \"\";\n        }\n        if (!inputname.equals(\"\")) {\n            fieldName = outputname.substring(inputname.length() + 1);\n        }\n        return fieldName;\n    }\n\n    /**\n     * This is called after instantiating the class that is actually in the parsetree.\n     * @param newInstance The new instances of this class that must be initialized\n     */\n    protected void initializeNewInstance(Dissector newInstance) throws InvalidDissectorException {\n        // Default behaviour is do nothing.\n    }\n\n    /**\n     * If a dissector really needs to add an additional dissector to the set this method is the\n     * place to do so.\n     * @param parser The instance of the parser where the extra dissector is to be added to.\n     * @param <RECORD> The type of the record.\n     */\n    public <RECORD> void createAdditionalDissectors(Parser<RECORD> parser) {\n        // Default behaviour is do nothing.\n    }\n\n    public void setInputType(String s) throws InvalidDissectorException {\n        // Usually only implemented in very dynamic dissectors (like custom timestamp format)\n        throw new InvalidDissectorException(\"The InputType of \" + this.getClass().getCanonicalName() + \" cannot be changed\");\n    }\n\n    @Override\n    public String toString() {\n        return \"{ \" + this.getClass().getSimpleName() + \" : \" + getInputType() + \" --> \" + getPossibleOutput() + \" }\";\n    }\n}\n"
  },
  {
    "path": "parser-core/src/main/java/nl/basjes/parse/core/Field.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.core;\n\nimport nl.basjes.parse.core.Parser.SetterPolicy;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n@Retention(RetentionPolicy.RUNTIME)\n@Target({ElementType.METHOD})\n/*\n * Defines the source field that must be used to call this setter.\n */\npublic @interface Field {\n    String[] value();\n\n    SetterPolicy setterPolicy() default SetterPolicy.ALWAYS;\n}\n"
  },
  {
    "path": "parser-core/src/main/java/nl/basjes/parse/core/Parsable.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.core;\n\nimport nl.basjes.parse.core.exceptions.DissectionFailure;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.TreeMap;\n\npublic final class Parsable<RECORD> {\n\n    private static final Logger LOG = LoggerFactory.getLogger(Parsable.class);\n\n    private final Parser<RECORD>           parser;\n\n    // The actual record for which all the information is intended.\n    private final RECORD                   record;\n\n    // This caches the values and intermediate values\n    private final Map<String, ParsedField> cache      = new TreeMap<>();\n\n    // The end nodes we really need as output\n    // Values look like \"TYPE:foo.bar\"\n    private final Set<String>              needed;\n\n    // Values look like \"TYPE:foo.bar\"\n    private final Set<String>              usefulIntermediates;\n\n    // The set of ParsedFields that need to be parsed further\n    private final Set<ParsedField>         toBeParsed = new HashSet<>();\n\n    private final Map<String, Set<String>> typeRemappings;\n\n    // --------------------------------------------\n\n    public Parsable(final Parser<RECORD> parser, final RECORD record, Map<String, Set<String>> typeRemappings) {\n        this.parser = parser;\n        this.record = record;\n        this.typeRemappings = typeRemappings;\n        needed = parser.getNeeded();\n        usefulIntermediates = parser.getUsefulIntermediateFields();\n    }\n\n    // --------------------------------------------\n    /** Store a newly parsed value in the result set */\n    void setRootDissection(final String type, final String value) {\n        LOG.debug(\"Got root dissection: type={}\", type);\n\n        // The root name is an empty string\n        final ParsedField parsedfield = new ParsedField(type, \"\", value);\n\n        cache.put(parsedfield.getId(), parsedfield);\n        toBeParsed.add(parsedfield);\n    }\n\n    // --------------------------------------------\n\n    /** Store a newly parsed value in the result set */\n    public Parsable<RECORD> addDissection(final String base, final String type, final String name, final int value) throws DissectionFailure {\n        LOG.debug(\"Got new (int) dissection: base={}; type={}; name=\\\"{}\\\"\", base, type, name);\n        return addDissection(base, type, name, new Value((long)value), false);\n    }\n\n    /** Store a newly parsed value in the result set */\n    public Parsable<RECORD> addDissection(final String base, final String type, final String name, final Integer value) throws DissectionFailure {\n        LOG.debug(\"Got new (Integer) dissection: base={}; type={}; name=\\\"{}\\\"\", base, type, name);\n        if (value == null) {\n            return addDissection(base, type, name, new Value((Long)null), false);\n        }\n        return addDissection(base, type, name, new Value((long)value), false);\n    }\n\n    /** Store a newly parsed value in the result set */\n    public Parsable<RECORD> addDissection(final String base, final String type, final String name, final long value) throws DissectionFailure {\n        LOG.debug(\"Got new (long) dissection: base={}; type={}; name=\\\"{}\\\"\", base, type, name);\n        return addDissection(base, type, name, new Value(value), false);\n    }\n\n    /** Store a newly parsed value in the result set */\n    public Parsable<RECORD> addDissection(final String base, final String type, final String name, final Long value) throws DissectionFailure {\n        LOG.debug(\"Got new (Long) dissection: base={}; type={}; name=\\\"{}\\\"\", base, type, name);\n        return addDissection(base, type, name, new Value(value), false);\n    }\n\n    /** Store a newly parsed value in the result set */\n    public Parsable<RECORD> addDissection(final String base, final String type, final String name, final float value) throws DissectionFailure {\n        LOG.debug(\"Got new (float) dissection: base={}; type={}; name=\\\"{}\\\"\", base, type, name);\n        return addDissection(base, type, name, new Value((double)value), false);\n    }\n\n    /** Store a newly parsed value in the result set */\n    public Parsable<RECORD> addDissection(final String base, final String type, final String name, final Float value) throws DissectionFailure {\n        LOG.debug(\"Got new (Float) dissection: base={}; type={}; name=\\\"{}\\\"\", base, type, name);\n        if (value == null) {\n            return addDissection(base, type, name, new Value((Double)null), false);\n        }\n        return addDissection(base, type, name, new Value((double)value), false);\n    }\n\n    /** Store a newly parsed value in the result set */\n    public Parsable<RECORD> addDissection(final String base, final String type, final String name, final double value) throws DissectionFailure {\n        LOG.debug(\"Got new (double) dissection: base={}; type={}; name=\\\"{}\\\"\", base, type, name);\n        return addDissection(base, type, name, new Value(value), false);\n    }\n\n    /** Store a newly parsed value in the result set */\n    public Parsable<RECORD> addDissection(final String base, final String type, final String name, final Double value) throws DissectionFailure {\n        LOG.debug(\"Got new (Double) dissection: base={}; type={}; name=\\\"{}\\\"\", base, type, name);\n        return addDissection(base, type, name, new Value(value), false);\n    }\n\n    /** Store a newly parsed value in the result set */\n    public Parsable<RECORD> addDissection(final String base, final String type, final String name, final String value) throws DissectionFailure {\n        LOG.debug(\"Got new (String) dissection: base={}; type={}; name=\\\"{}\\\"\", base, type, name);\n        return addDissection(base, type, name, new Value(value), false);\n    }\n\n    /** Store a newly parsed value in the result set */\n    public Parsable<RECORD> addDissection(final String base, final String type, final String name, final Value value) throws DissectionFailure {\n        LOG.debug(\"Got new (Value) dissection: base={}; type={}; name=\\\"{}\\\"\", base, type, name);\n        return addDissection(base, type, name, value, false);\n    }\n\n    private Parsable<RECORD> addDissection(\n            final String base,\n            final String type,\n            final String name,\n            final Value value,\n            final boolean recursion)\n            throws DissectionFailure {\n        String completeName;\n        String neededWildCardName;\n        if (base.isEmpty()) { // The root name is an empty string\n            completeName = name;\n            neededWildCardName = type + ':' + \"*\";\n        } else {\n            if (name.isEmpty()) {\n                completeName = base;\n            } else {\n                completeName = base + '.' + name;\n            }\n            neededWildCardName = type + ':' + base + \".*\";\n        }\n        String neededName = type + ':' + completeName;\n\n        if (!recursion) {\n            if (typeRemappings.containsKey(completeName)) {\n                Set<String> typeRemappingSet = typeRemappings.get(completeName);\n                for (String typeRemapping : typeRemappingSet) {\n                    if (type.equals(typeRemapping)) {\n                        throw new DissectionFailure(\n                                \"[Type Remapping] Trying to map to the same type (mapping definition bug!): \" +\n                                        \" base=\" + base + \" type=\" + type + \" name=\" + name);\n                    }\n                    addDissection(base, typeRemapping, name, value, true);\n                }\n            }\n        }\n\n        final ParsedField parsedfield = new ParsedField(type, completeName, value);\n\n        if (usefulIntermediates.contains(completeName)) {\n            cache.put(parsedfield.getId(), parsedfield);\n            toBeParsed.add(parsedfield);\n        }\n\n        if (needed.contains(neededName)) {\n            parser.store(record, neededName, neededName, value);\n        }\n\n        if (needed.contains(neededWildCardName)) {\n            parser.store(record, neededWildCardName, neededName, value);\n        }\n        return this;\n    }\n\n    // --------------------------------------------\n\n    public ParsedField getParsableField(final String type, final String name) {\n        return cache.get(ParsedField.makeId(type, name));\n    }\n\n    // --------------------------------------------\n\n    public RECORD getRecord() {\n        return record;\n    }\n\n    // --------------------------------------------\n\n    public void setAsParsed(final ParsedField parsedField) {\n        toBeParsed.remove(parsedField);\n    }\n\n    // --------------------------------------------\n\n    public Set<ParsedField> getToBeParsed() {\n        return toBeParsed;\n    }\n\n}\n"
  },
  {
    "path": "parser-core/src/main/java/nl/basjes/parse/core/ParsedField.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.core;\n\npublic class ParsedField {\n\n    private final String  type;\n    private final String  name;\n    private final Value   value;\n\n    public ParsedField(String type, String name, Value value) {\n        this.type = type;\n        this.name = name;\n        if (value==null) {\n            this.value = new Value((String)null);\n        } else {\n            this.value = value;\n        }\n    }\n\n    public ParsedField(String type, String name, String value) {\n        this.type = type;\n        this.name = name;\n        this.value = new Value(value);\n    }\n\n    public String getType() {\n        return type;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public Value getValue() {\n        return value;\n    }\n\n    public static String makeId(String type, String name) {\n        return type+':'+name;\n    }\n\n    public String getId() {\n        return makeId(type, name);\n    }\n\n    public String toString(){\n        return getId() + \" = \" + value;\n    }\n\n}\n"
  },
  {
    "path": "parser-core/src/main/java/nl/basjes/parse/core/Parser.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.core;\n\nimport nl.basjes.parse.core.exceptions.DissectionFailure;\nimport nl.basjes.parse.core.exceptions.FatalErrorDuringCallOfSetterMethod;\nimport nl.basjes.parse.core.exceptions.InvalidDissectorException;\nimport nl.basjes.parse.core.exceptions.InvalidFieldMethodSignature;\nimport nl.basjes.parse.core.exceptions.MissingDissectorsException;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.io.Serializable;\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.Method;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.EnumSet;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.Set;\nimport java.util.TreeMap;\nimport java.util.stream.Collectors;\n\nimport static nl.basjes.parse.core.Casts.STRING_ONLY;\nimport static nl.basjes.parse.core.Parser.SetterPolicy.ALWAYS;\nimport static nl.basjes.parse.core.Parser.SetterPolicy.NOT_EMPTY;\nimport static nl.basjes.parse.core.Parser.SetterPolicy.NOT_NULL;\n\npublic class Parser<RECORD> implements Serializable {\n\n    public enum SetterPolicy {\n        /** Call the setter for all values: Normal, Empty and NULL */\n        ALWAYS,\n\n        /** Call the setter for values: Normal and Empty, but not for NULL values */\n        NOT_NULL,\n\n        /** Call the setter for values: Normal, but not for Empty and NULL values */\n        NOT_EMPTY\n    }\n\n    private static class DissectorPhase implements Serializable {\n        DissectorPhase(final String inputType, final String outputType, final String name, final Dissector instance) {\n            this.inputType  = inputType;\n            this.outputType = outputType;\n            this.name       = name;\n            this.instance   = instance;\n        }\n\n        private final String   inputType;\n        private final String   outputType;\n        private final String   name;\n        private final Dissector instance;\n    }\n\n    // --------------------------------------------\n\n    private static final Logger LOG = LoggerFactory.getLogger(Parser.class);\n\n    private final Class<RECORD> recordClass;\n\n    private final Set<DissectorPhase> availableDissectors = new HashSet<>();\n    private final Set<Dissector> allDissectors = new HashSet<>();\n\n    // Key = \"request.time.hour\"\n    // Value = the set of dissectors that must all be started once we have this value\n    private HashMap<String, Set<DissectorPhase>> compiledDissectors = null;\n    private HashSet<String> usefulIntermediateFields = null;\n    private String rootType;\n\n    // NOTE: The Method is NOT serializable. So after deserialization the 'assembled' is false\n    //       and we 're-find' all methods using their names and parameter lists.\n\n    // The target methods in the record class that will want to receive the values\n    private transient Map<String, Set<Pair<Method, SetterPolicy>>> targets = new TreeMap<>();\n    // Each method is a list of String: method name followed by the class names of each parameter.\n    private final Map<String, Set<Pair<List<String>, SetterPolicy>>> targetsMethodNames = new TreeMap<>();\n    private transient boolean assembled = false;\n\n    private final Map<String, EnumSet<Casts>> castsOfTargets = new TreeMap<>();\n\n\n    // --------------------------------------------\n\n    public Set<String> getNeeded() {\n        return targets.keySet();\n    }\n\n    /**\n     * Returns the casts possible for the specified path.\n     * Before you call 'getCasts' the actual parser needs to be constructed.\n     * Simply calling getPossiblePaths does not build the actual parser.\n     * If you want to get the casts for all possible paths the code looks something like this:\n     * <pre>{@code\n     * Parser<Object> dummyParser= new HttpdLoglineParser<>(Object.class, logformat);\n     * List<String> possiblePaths = dummyParser.getPossiblePaths();\n     * // Use a random method that has the right signature\n     * dummyParser.addParseTarget(String.class.getMethod(\"indexOf\", String.class), possiblePaths);\n     * for (String path : possiblePaths) {\n     *     LOG.info(\"{}     {}\", path, dummyParser.getCasts(path));\n     * }\n     * }</pre>\n     * @param name The name of the path for which you want the casts\n     * @return The set of casts that are valid for this name. Null if this name is unknown.\n     */\n    public EnumSet<Casts> getCasts(String name) throws MissingDissectorsException, InvalidDissectorException {\n        assembleDissectors();\n        return castsOfTargets.get(name);\n    }\n\n    public Map<String, EnumSet<Casts>> getAllCasts() throws MissingDissectorsException, InvalidDissectorException {\n        assembleDissectors();\n        return castsOfTargets;\n    }\n\n    // --------------------------------------------\n\n    Set<String> getUsefulIntermediateFields() {\n        return usefulIntermediateFields;\n    }\n\n    // --------------------------------------------\n\n    public final Parser<RECORD> addDissectors(final List<Dissector> dissectors) {\n        assembled = false;\n        if (dissectors != null) {\n            allDissectors.addAll(dissectors);\n        }\n        return this;\n    }\n\n    // --------------------------------------------\n\n    public final Parser<RECORD> addDissector(final Dissector dissector) {\n        assembled = false;\n        if (dissector != null) {\n            allDissectors.add(dissector);\n        }\n        return this;\n    }\n\n    // --------------------------------------------\n\n    public final Parser<RECORD> dropDissector(Class<? extends Dissector> dissectorClassToDrop) {\n        assembled = false;\n        Set<Dissector> removeDissector = new HashSet<>();\n        for (final Dissector dissector : allDissectors) {\n            if (dissector.getClass().equals(dissectorClassToDrop)) {\n                removeDissector.add(dissector);\n            }\n        }\n        allDissectors.removeAll(removeDissector);\n        return this;\n    }\n\n    // --------------------------------------------\n\n    public final Set<Dissector> getAllDissectors() {\n        return allDissectors;\n    }\n\n    // --------------------------------------------\n\n    public Parser<RECORD> setRootType(final String newRootType) {\n        assembled = false;\n        rootType = newRootType;\n        return this;\n    }\n\n    // --------------------------------------------\n    private void assembleDissectorPhases() throws InvalidDissectorException {\n        availableDissectors.clear();\n        for (final Dissector dissector : allDissectors) {\n            final String inputType = dissector.getInputType();\n            if (inputType == null) {\n                throw new InvalidDissectorException(\"Dissector returns null on getInputType(): [\"+ dissector.getClass().getCanonicalName()+\"]\");\n            }\n\n            final List<String> outputs = dissector.getPossibleOutput();\n            if (outputs == null || outputs.isEmpty()) {\n                throw new InvalidDissectorException(\"Dissector cannot create any outputs: [\"+ dissector.getClass().getCanonicalName()+\"]\");\n            }\n\n            // Create all dissector phases\n            for (final String output: outputs) {\n                final int colonPos = output.indexOf(':');\n                final String outputType = output.substring(0, colonPos);\n                final String name = output.substring(colonPos + 1);\n                availableDissectors.add(new DissectorPhase(inputType, outputType, name, dissector));\n            }\n        }\n    }\n\n    // --------------------------------------------\n\n    private boolean failOnMissingDissectors = true;\n\n    /**\n     * This method sets the flag to ignore the missing dissectors situation.\n     * This must be called before parsing the first line.\n     * The effect is that those fields that would have been classified as 'missing'\n     * will result in a null value (or better: the setter is never called) for all records.\n     */\n    public Parser<RECORD> ignoreMissingDissectors() {\n        failOnMissingDissectors = false;\n        return this;\n    }\n\n    /**\n     * Reset back to the default of failing on missing dissectors.\n     */\n    public Parser<RECORD> failOnMissingDissectors() {\n        failOnMissingDissectors = true;\n        return this;\n    }\n\n\n    private void assembleDissectors() throws MissingDissectorsException, InvalidDissectorException {\n        if (assembled) {\n            return; // nothing to do.\n        }\n\n        if (targets == null) {\n            // This happens only AFTER deserialization.\n            targets = new HashMap<>(targetsMethodNames.size());\n\n            for (Entry<String, Set<Pair<List<String>, SetterPolicy>>> entry:targetsMethodNames.entrySet()) {\n\n                String fieldName = entry.getKey();\n                Set<Pair<List<String>, SetterPolicy>> methodSet = entry.getValue();\n\n                Set<Pair<Method, SetterPolicy>> fieldTargets = targets.computeIfAbsent(fieldName, k -> new HashSet<>());\n\n                for(Pair<List<String>, SetterPolicy> methodStringPair: methodSet) {\n                    List<String> methodString = methodStringPair.getLeft();\n                    SetterPolicy setterPolicy = methodStringPair.getRight();\n                    Method method;\n                    String methodName = methodString.get(0);\n                    int numberOfParameters = methodString.size()-1;\n                    Class<?>[] parameters = new Class[numberOfParameters];\n                    try {\n                        parameters[0] = Class.forName(methodString.get(1));\n                        if (numberOfParameters == 2) {\n                            parameters[1] = Class.forName(methodString.get(2));\n                        }\n                    } catch (ClassNotFoundException e) {\n                        throw new InvalidDissectorException(\"Unable to locate class\", e);\n                    }\n                    try {\n                        method = recordClass.getMethod(methodName, parameters);\n                        fieldTargets.add(Pair.of(method, setterPolicy));\n                    } catch (NoSuchMethodException e) {\n                        throw new InvalidDissectorException(\"Unable to locate method \" + methodName, e);\n                    }\n                }\n                targets.put(fieldName, fieldTargets);\n            }\n        }\n\n        // In some cases a dissector may need to create a special 'extra' dissector.\n        // Which in some cases this is a recursive problem\n        Set<Dissector> doneDissectors = new HashSet<>(allDissectors.size() + 10);\n        Set<Dissector> loopDissectors = new HashSet<>(allDissectors);\n\n        while (!loopDissectors.isEmpty()) {\n            for (final Dissector dissector : loopDissectors) {\n                dissector.createAdditionalDissectors(this);\n            }\n            doneDissectors.addAll(loopDissectors);\n            loopDissectors.clear();\n            loopDissectors.addAll(allDissectors);\n            loopDissectors.removeAll(doneDissectors);\n        }\n\n        // So\n        // - we have a set of needed values (targets)\n        // - we have a set of dissectors that can pick apart some input\n        // - we know where to start from\n        // - we need to know how to proceed\n\n        assembleDissectorPhases();\n\n        // Step 1: Acquire all potentially useful subtargets\n        // We first build a set of all possible subtargets that may be useful\n        // this way we can skip anything we know not to be useful\n        Set<String> needed = new HashSet<>(getNeeded());\n        needed.add(rootType + ':'); // The root name is an empty string\n        LOG.debug(\"Root: >>>{}:<<<\", rootType);\n\n        Set<String> allPossibleSubtargets = new HashSet<>();\n        for (String need : needed) {\n            String neededName = need.substring(need.indexOf(':') + 1);\n            LOG.debug(\"Needed  : >>>{}<<<\", neededName);\n            String[] needs = neededName.split(\"\\\\.\");\n            StringBuilder sb = new StringBuilder(need.length());\n\n            for (String part : needs) {\n                if (sb.length() == 0 || part.length() == 0) {\n                    sb.append(part);\n                } else {\n                    sb.append('.').append(part);\n                }\n                allPossibleSubtargets.add(sb.toString());\n                LOG.debug(\"Possible: >>>{}<<<\", sb);\n            }\n        }\n\n        // Step 2: From the root we explore all possibly useful trees (recursively)\n        compiledDissectors = new HashMap<>();\n        usefulIntermediateFields = new HashSet<>();\n        Set<String> locatedTargets = new HashSet<>();\n        findUsefulDissectorsFromField(allPossibleSubtargets, locatedTargets, rootType, \"\", true); // The root name is an empty string\n\n        // Step 3: Inform all dissectors to prepare for the run\n        for (Set<DissectorPhase> dissectorPhases : compiledDissectors.values()) {\n            for (DissectorPhase dissectorPhase : dissectorPhases) {\n                dissectorPhase.instance.prepareForRun();\n            }\n        }\n\n        if (compiledDissectors == null || compiledDissectors.isEmpty()) {\n            throw new MissingDissectorsException(\"There are no dissectors at all which makes this a completely useless parser.\");\n        }\n\n        if (failOnMissingDissectors) {\n            // Step 4: As a final step we verify that every required input can be found\n            Set<String> missingDissectors = getTheMissingFields(locatedTargets);\n            if (!missingDissectors.isEmpty()) {\n                StringBuilder allMissing = new StringBuilder(missingDissectors.size() * 64);\n                for (String missing : missingDissectors) {\n                    allMissing.append('\\n').append(missing);\n                }\n                throw new MissingDissectorsException(allMissing.toString());\n            }\n        }\n        assembled = true;\n    }\n\n    // --------------------------------------------\n\n    private void findUsefulDissectorsFromField(\n            final Set<String> possibleTargets,\n            final Set<String> locatedTargets,\n            final String subRootType, final String subRootName,\n            final boolean thisIsTheRoot) throws InvalidDissectorException {\n\n        String subRootId = subRootType + ':' + subRootName;\n\n        // When we reach this point we have dissectors to get here.\n        // So we store this to later validate if we have everything.\n        if (locatedTargets.contains(subRootId)) {\n            // We already found this one.\n            return; // Avoid infinite recursion\n        }\n        locatedTargets.add(subRootId);\n\n        LOG.debug(\"findUsefulDissectors:\\\"{}\\\" \\\"{}\\\"\", subRootType, subRootName);\n\n        for (DissectorPhase dissector: availableDissectors) {\n\n            if (!(dissector.inputType.equals(subRootType))) {\n                continue; // Wrong type\n            }\n\n            // If it starts with a . it extends.\n            // If it doesn't then it starts at the beginning\n            Set<String> checkFields = new HashSet<>();\n\n            // If true then this dissector can output any name instead of just one\n            boolean isWildCardDissector = dissector.name.equals(\"*\");\n\n            if (isWildCardDissector) {\n                // Ok, this is special\n                // We need to see if any of the wanted types start with the\n                // subRootName (it may have a '.' in the rest of the line !)\n                String subRootNameMatch = subRootName + '.';\n                for (String possibleTarget : possibleTargets) {\n                    if (possibleTarget.startsWith(subRootNameMatch)) {\n                        checkFields.add(possibleTarget);\n                    }\n                }\n            } else if (thisIsTheRoot) {\n                checkFields.add(dissector.name);\n            } else if (dissector.name.isEmpty()) {\n                checkFields.add(subRootName);\n            } else {\n                checkFields.add(subRootName + '.' + dissector.name);\n            }\n\n            for (String checkField: checkFields) {\n                if (possibleTargets.contains(checkField)\n                    && !compiledDissectors.containsKey(dissector.outputType + \":\" + checkField)) {\n\n                    Set<DissectorPhase> subRootPhases = compiledDissectors.get(subRootId);\n                    if (subRootPhases == null) {\n                        // New so we can simply add it.\n                        subRootPhases = new HashSet<>();\n                        compiledDissectors.put(subRootId, subRootPhases);\n                        usefulIntermediateFields.add(subRootName);\n                    }\n\n                    Class<? extends Dissector> clazz = dissector.instance.getClass();\n                    DissectorPhase dissectorPhaseInstance = findDissectorInstance(subRootPhases, clazz);\n\n                    if (dissectorPhaseInstance == null) {\n                        dissectorPhaseInstance =\n                                new DissectorPhase(dissector.inputType, dissector.outputType,\n                                        checkField, dissector.instance.getNewInstance());\n                        subRootPhases.add(dissectorPhaseInstance);\n                    }\n\n                    // Tell the dissector instance what to expect\n                    if (LOG.isDebugEnabled()) {\n                        LOG.debug(\"Informing : ({}){} --> {} --> ({}){}\",\n                                dissector.inputType, subRootName,\n                                dissector.instance.getClass().getName(),\n                                dissector.outputType, checkField);\n                    }\n                    castsOfTargets.put(dissector.outputType + ':' + checkField,\n                            dissectorPhaseInstance.instance.prepareForDissect(subRootName, checkField));\n\n                    // Recurse from this point down\n                    findUsefulDissectorsFromField(possibleTargets, locatedTargets, dissector.outputType, checkField, false);\n                }\n            }\n        }\n\n        Set<String> mappings = typeRemappings.get(subRootName);\n        if (mappings != null) {\n            for (String mappedType : mappings) {\n                if (!compiledDissectors.containsKey(mappedType + ':' + subRootName)) {\n                    // Retyped targets are ALWAYS String ONLY if they do not yet exist.\n                    castsOfTargets.putIfAbsent(mappedType + ':' + subRootName, STRING_ONLY);\n                    findUsefulDissectorsFromField(possibleTargets, locatedTargets, mappedType, subRootName, false);\n                }\n            }\n        }\n\n    }\n\n    private DissectorPhase findDissectorInstance(Set<DissectorPhase> dissectorPhases,\n                                                 Class<? extends Dissector> clazz) {\n        for (DissectorPhase phase : dissectorPhases) {\n            if (phase.instance.getClass() == clazz) {\n                return phase;\n            }\n        }\n        return null;\n    }\n\n    // --------------------------------------------\n\n    private Set<String> getTheMissingFields(Set<String> locatedTargets) {\n        Set<String> missing = new HashSet<>();\n        for (String target : getNeeded()) {\n            if (!locatedTargets.contains(target)) {\n                // Handle wildcard targets differently\n                if (target.endsWith(\"*\")) {\n                    if (target.endsWith(\".*\")) {\n                        if (!locatedTargets.contains(target.substring(0, target.length() - 2))) {\n                            missing.add(target);\n                        }\n                    }\n                    // Else: it ends with :* and it is always \"present\".\n                } else {\n                    missing.add(target);\n                }\n            }\n        }\n        return missing;\n    }\n\n    // --------------------------------------------\n\n    /*\n     * The constructor tries to retrieve the desired fields from the annotations in the specified class. */\n    public Parser(final Class<RECORD> clazz) {\n        recordClass = clazz;\n\n        // Get all methods of the correct signature that have been annotated\n        // with Field\n        for (final Method method : recordClass.getMethods()) {\n            final Field field = method.getAnnotation(Field.class);\n            if (field != null) {\n                addParseTarget(method, field.setterPolicy(), Arrays.asList(field.value()));\n            }\n        }\n    }\n\n    // --------------------------------------------\n\n    /*\n     * When there is a need to add a target callback manually use this method. */\n    public Parser<RECORD> addParseTarget(final String setterMethodName,\n                               final String fieldValue) throws NoSuchMethodException {\n        addParseTarget(setterMethodName, ALWAYS, fieldValue);\n        return this;\n    }\n\n    /*\n     * When there is a need to add a target callback manually use this method. */\n    public Parser<RECORD> addParseTarget(final String setterMethodName,\n                               final SetterPolicy setterPolicy,\n                               final String fieldValue) throws NoSuchMethodException {\n        Method method;\n\n        try {\n            method = recordClass.getMethod(setterMethodName, String.class);\n        } catch (NoSuchMethodException a) {\n            try {\n                method = recordClass.getMethod(setterMethodName, String.class, String.class);\n            } catch (NoSuchMethodException b) {\n                try {\n                    method = recordClass.getMethod(setterMethodName, String.class, Long.class);\n                } catch (NoSuchMethodException c) {\n                    try {\n                        method = recordClass.getMethod(setterMethodName, String.class, Double.class);\n                    } catch (NoSuchMethodException d) {\n                        try {\n                            method = recordClass.getMethod(setterMethodName, Long.class);\n                        } catch (NoSuchMethodException e) {\n                            try {\n                                method = recordClass.getMethod(setterMethodName, Double.class);\n                            } catch (NoSuchMethodException f) {\n                                throw new NoSuchMethodException(\n                                    \"Unable to find any valid form of the method \" + setterMethodName +\n                                        \" in the class \" + recordClass.getCanonicalName());\n                            }\n                        }\n                    }\n                }\n            }\n        }\n\n        addParseTarget(method, setterPolicy, Collections.singletonList(fieldValue));\n        return this;\n    }\n\n\n    /*\n     * When there is a need to add a target callback manually use this method. */\n    public Parser<RECORD> addParseTarget(final Method method, final String fieldValue) {\n        return addParseTarget(method, SetterPolicy.ALWAYS, Collections.singletonList(fieldValue));\n    }\n\n    /*\n     * When there is a need to add a target callback manually use this method. */\n    public Parser<RECORD> addParseTarget(final Method method,\n                               final SetterPolicy setterPolicy,\n                               final String fieldValue) {\n        return addParseTarget(method, setterPolicy, Collections.singletonList(fieldValue));\n    }\n\n    /*\n     * When there is a need to add a target callback manually use this method. */\n    public Parser<RECORD> addParseTarget(final Method method, final List<String> fieldValues) {\n        return addParseTarget(method, SetterPolicy.ALWAYS, fieldValues);\n    }\n\n    /*\n     * When there is a need to add a target callback manually use this method. */\n    public Parser<RECORD> addParseTarget(final Method method,\n                               final SetterPolicy setterPolicy,\n                               final List<String> fieldValues) {\n        assembled = false;\n\n        if (method == null || fieldValues == null) {\n            return this; // Nothing to do here\n        }\n\n        final Class<?>[] parameters = method.getParameterTypes();\n        if (\n                // Setters that receive a String\n                ((parameters.length == 1) && (parameters[0] == String.class)) ||\n                ((parameters.length == 2) && (parameters[0] == String.class) && (parameters[1] == String.class)) ||\n\n                // Setters that receive a Long\n                ((parameters.length == 1) && (parameters[0] == Long.class)) ||\n                ((parameters.length == 2) && (parameters[0] == String.class) && (parameters[1] == Long.class)) ||\n\n                // Setters that receive a Double\n                ((parameters.length == 1) && (parameters[0] == Double.class)) ||\n                ((parameters.length == 2) && (parameters[0] == String.class) && (parameters[1] == Double.class))\n        ) {\n            for (final String fieldValue : fieldValues) {\n                if (fieldValue == null) {\n                    continue;\n                }\n                String cleanedFieldValue = cleanupFieldValue(fieldValue);\n                if (!fieldValue.equals(cleanedFieldValue)) {\n                    LOG.warn(\"The requested \\\"{}\\\" was converted into \\\"{}\\\"\", fieldValue, cleanedFieldValue);\n                }\n\n                // We have 1 real target\n                Set<Pair<Method, SetterPolicy>> fieldTargets = targets.computeIfAbsent(cleanedFieldValue, k -> new HashSet<>());\n                fieldTargets.add(Pair.of(method, setterPolicy));\n                targets.put(cleanedFieldValue, fieldTargets);\n\n                // We have 1 real target\n                Set<Pair<List<String>, SetterPolicy>> fieldTargetNames = targetsMethodNames.get(cleanedFieldValue);\n                if (fieldTargetNames == null) {\n                    fieldTargetNames = new HashSet<>();\n                }\n                List<String> methodList = new ArrayList<>();\n                methodList.add(method.getName());\n                for (Class<?> clazz: method.getParameterTypes()) {\n                    methodList.add(clazz.getCanonicalName());\n                }\n                fieldTargetNames.add(Pair.of(methodList, setterPolicy));\n                targetsMethodNames.put(cleanedFieldValue, fieldTargetNames);\n            }\n        } else {\n            throw new InvalidFieldMethodSignature(method);\n        }\n        return this;\n    }\n\n    // --------------------------------------------\n\n    private Map<String, Set<String>> typeRemappings = new HashMap<>(16);\n\n    public Parser<RECORD> setTypeRemappings(Map<String, Set<String>> pTypeRemappings) {\n        if (pTypeRemappings == null) {\n            this.typeRemappings.clear();\n        } else {\n            this.typeRemappings = pTypeRemappings;\n        }\n        return this;\n    }\n\n    public Parser<RECORD> addTypeRemappings(Map<String, Set<String>> additionalTypeRemappings) {\n        for (Entry<String, Set<String>> entry: additionalTypeRemappings.entrySet()){\n            String input = entry.getKey();\n            for (String newType: entry.getValue()) {\n                addTypeRemapping(input, newType, STRING_ONLY);\n            }\n        }\n        return this;\n    }\n\n    public Parser<RECORD> addTypeRemapping(String input, String newType) {\n        return addTypeRemapping(input, newType, STRING_ONLY);\n    }\n\n    public Parser<RECORD> addTypeRemapping(String input, String newType, EnumSet<Casts> newCasts) {\n        assembled = false;\n\n        String theInput = input.trim().toLowerCase(Locale.ENGLISH);\n        String theType = newType.trim().toUpperCase(Locale.ENGLISH);\n\n        Set<String> mappingsForInput = typeRemappings.computeIfAbsent(theInput, k -> new HashSet<>());\n\n        if (!mappingsForInput.contains(theType)) {\n            mappingsForInput.add(theType);\n            castsOfTargets.put(theType+':'+theInput, newCasts);\n        }\n        return this;\n    }\n\n    // --------------------------------------------\n\n    public static String cleanupFieldValue(String fieldValue) {\n        final int colonPos = fieldValue.indexOf(':');\n        if (colonPos == -1) {\n            return fieldValue.toLowerCase(Locale.ENGLISH);\n        }\n\n        final String fieldType = fieldValue.substring(0, colonPos);\n        final String fieldName = fieldValue.substring(colonPos + 1);\n\n        return fieldType.toUpperCase(Locale.ENGLISH)+':'+ fieldName.toLowerCase(Locale.ENGLISH);\n    }\n\n    // --------------------------------------------\n\n\n    /**\n     * Parse the value and return a new instance of RECORD.\n     * For this method to work the RECORD class may NOT be an inner class.\n     */\n    public RECORD parse(final String value)\n        throws DissectionFailure, InvalidDissectorException, MissingDissectorsException {\n        assembleDissectors();\n        final Parsable<RECORD> parsable = createParsable();\n        if (parsable == null) {\n            return null;\n        }\n        parsable.setRootDissection(rootType, value);\n        return parse(parsable).getRecord();\n    }\n\n    // --------------------------------------------\n\n    /**\n     * Parse the value and call all configured setters in the provided instance of RECORD.\n     */\n    public RECORD parse(final RECORD record, final String value)\n        throws DissectionFailure, InvalidDissectorException, MissingDissectorsException {\n        assembleDissectors();\n        final Parsable<RECORD> parsable = createParsable(record);\n        parsable.setRootDissection(rootType, value);\n        return parse(parsable).getRecord();\n    }\n\n    // --------------------------------------------\n\n    Parsable<RECORD> parse(final Parsable<RECORD> parsable)\n        throws DissectionFailure, InvalidDissectorException, MissingDissectorsException {\n        assembleDissectors();\n\n        if (!assembled) {\n            return null;\n        }\n\n        // Values look like \"TYPE:foo.bar\"\n        Set<ParsedField> toBeParsed = new HashSet<>(parsable.getToBeParsed());\n\n        while (!toBeParsed.isEmpty()) {\n            for (ParsedField fieldThatNeedsToBeParsed : toBeParsed) {\n                parsable.setAsParsed(fieldThatNeedsToBeParsed);\n                Set<DissectorPhase> dissectorSet = compiledDissectors.get(fieldThatNeedsToBeParsed.getId());\n                if (dissectorSet != null) {\n                    for (DissectorPhase dissector : dissectorSet) {\n                        if (LOG.isDebugEnabled()) {\n                            LOG.debug(\"Dissect {} with {}\", fieldThatNeedsToBeParsed, dissector.instance.getClass().getName());\n                        }\n                        dissector.instance.dissect(parsable, fieldThatNeedsToBeParsed.getName());\n                    }\n                } else {\n                    LOG.trace(\"NO DISSECTORS FOR \\\"{}\\\"\", fieldThatNeedsToBeParsed);\n                }\n            }\n            toBeParsed.clear();\n            toBeParsed.addAll(parsable.getToBeParsed());\n        }\n        return parsable;\n    }\n\n    // --------------------------------------------\n\n    void store(final RECORD record, final String key, final String name, final Value value) {\n        boolean calledASetter = false;\n\n        if (value == null) {\n            LOG.error(\"Got a null value to store for key={}  name={}.\", key, name);\n            return; // Nothing to do\n        }\n\n        final Set<Pair<Method, SetterPolicy>> methodPairs = targets.get(key);\n        if (methodPairs == null) {\n            LOG.error(\"NO methods for key={}  name={}.\", key, name);\n            return;\n        }\n\n        EnumSet<Casts> castsTo = castsOfTargets.get(key);\n        if (castsTo == null) {\n            castsTo = castsOfTargets.get(name);\n            if (castsTo == null) {\n                LOG.error(\"NO casts for \\\"{}\\\"\", name);\n                return;\n            }\n        }\n\n        for (Pair<Method, SetterPolicy> methodPair : methodPairs) {\n            Method method = methodPair.getLeft();\n            if (method != null) {\n                SetterPolicy setterPolicy = methodPair.getRight();\n                try {\n                    Class<?>[] parameters = method.getParameterTypes();\n                    Class<?> valueClass = parameters[parameters.length - 1]; // Always the last one\n\n                    if (valueClass == String.class) {\n                        if (castsTo.contains(Casts.STRING)) {\n                            String stringValue = value.getString();\n                            if (stringValue == null) {\n                                if (setterPolicy == NOT_NULL || setterPolicy == NOT_EMPTY) {\n                                    calledASetter = true;\n                                    continue;\n                                }\n                            } else {\n                                if (stringValue.isEmpty() && setterPolicy == NOT_EMPTY) {\n                                    calledASetter = true;\n                                    continue;\n                                }\n                            }\n                            if (parameters.length == 2) {\n                                method.invoke(record, name, stringValue);\n                            } else {\n                                method.invoke(record, stringValue);\n                            }\n                            calledASetter = true;\n                        }\n                        continue;\n                    }\n\n                    if (valueClass == Long.class) {\n                        if (castsTo.contains(Casts.LONG)) {\n                            Long longValue = value.getLong();\n                            if (longValue == null &&\n                                (setterPolicy == NOT_NULL || setterPolicy == NOT_EMPTY)) {\n                                calledASetter = true;\n                                continue;\n                            }\n                            if (parameters.length == 2) {\n                                method.invoke(record, name, longValue);\n                            } else {\n                                method.invoke(record, longValue);\n                            }\n                            calledASetter = true;\n                        }\n                        continue;\n                    }\n\n                    if (valueClass == Double.class) {\n                        if (castsTo.contains(Casts.DOUBLE)) {\n                            Double doubleValue = value.getDouble();\n                            if (doubleValue == null &&\n                                (setterPolicy == NOT_NULL || setterPolicy == NOT_EMPTY)) {\n                                calledASetter = true;\n                                continue;\n                            }\n                            if (parameters.length == 2) {\n                                method.invoke(record, name, doubleValue);\n                            } else {\n                                method.invoke(record, doubleValue);\n                            }\n                            calledASetter = true;\n                        }\n                        continue;\n                    }\n\n                    throw new FatalErrorDuringCallOfSetterMethod(\n                            \"Tried to call setter with unsupported class :\" +\n                            \" key = \\\"\" + key + \"\\\" \" +\n                            \" name = \\\"\" + name + \"\\\" \" +\n                            \" value = \\\"\" + value + \"\\\"\" +\n                            \" castsTo = \\\"\" + castsTo + \"\\\"\");\n\n                } catch (final Exception e) {\n                    throw new FatalErrorDuringCallOfSetterMethod(e.getMessage() + \" caused by \\\"\" +\n                            e.getCause() + \"\\\" when calling \\\"\" +\n                            method.toGenericString() + \"\\\" for \" +\n                            \" key = \\\"\" + key + \"\\\" \" +\n                            \" name = \\\"\" + name + \"\\\" \" +\n                            \" value = \\\"\" + value + \"\\\"\" +\n                            \" castsTo = \\\"\" + castsTo + \"\\\"\", e);\n                }\n            }\n        }\n\n        if (!calledASetter) {\n            throw new FatalErrorDuringCallOfSetterMethod(\"No setter called for \" +\n                    \" key = \\\"\" + key + \"\\\" \" +\n                    \" name = \\\"\" + name + \"\\\" \" +\n                    \" value = \\\"\" + value + \"\\\"\");\n        }\n    }\n\n    // --------------------------------------------\n\n    private Parsable<RECORD> createParsable(RECORD record) {\n        return new Parsable<>(this, record, typeRemappings);\n    }\n\n    public Parsable<RECORD> createParsable() {\n        RECORD record;\n\n        try {\n            Constructor<RECORD> co = recordClass.getConstructor();\n            record = co.newInstance();\n        } catch (Exception e) {\n            LOG.error(\"Unable to create instance: {}\", e.getMessage());\n            return null;\n        }\n        return createParsable(record);\n    }\n\n    // --------------------------------------------\n\n    /**\n     * This method is for use by the developer to query the parser about\n     * the possible paths that may be extracted.\n     * @return A sorted list of all possible paths that could be determined automatically.\n     */\n    public List<String> getPossiblePaths() {\n        return getPossiblePaths(15, true);\n    }\n\n    /**\n     * This method is for use by the developer to query the parser about\n     * the possible paths that may be extracted.\n     * @param maxDepth The maximum recursion depth\n     * @return A sorted list of all possible paths that could be determined automatically.\n     */\n    public List<String> getPossiblePaths(int maxDepth) {\n        return getPossiblePaths(maxDepth, true);\n    }\n\n    /**\n     * This method is for use by the developer to query the parser about\n     * the possible paths that may be extracted.\n     * @param maxDepth The maximum recursion depth\n     * @param sortList Must the list of paths be sorted?\n     * @return A list of all possible paths that could be determined automatically.\n     */\n    public List<String> getPossiblePaths(int maxDepth, boolean sortList) {\n        if (allDissectors.isEmpty()) {\n            return Collections.emptyList(); // nothing to do.\n        }\n\n        try {\n            assembleDissectors();\n        } catch (MissingDissectorsException | InvalidDissectorException e) {\n            // Simply swallow this one\n        }\n        List<String> paths = new ArrayList<>();\n\n        Map<String, List<String>> pathNodes = new HashMap<>();\n\n        for (Dissector dissector : allDissectors) {\n            final String inputType = dissector.getInputType();\n            if (inputType == null) {\n                LOG.error(\"Dissector returns null on getInputType(): [{}]\", dissector.getClass().getCanonicalName());\n                return Collections.emptyList();\n            }\n\n            final List<String> outputs = dissector.getPossibleOutput();\n\n            if (LOG.isDebugEnabled()) {\n                LOG.debug(\"------------------------------------\");\n                LOG.debug(\"Possible: Dissector IN {}\", inputType);\n                for (String output: outputs) {\n                    LOG.debug(\"Possible:          --> {}\", output);\n                }\n            }\n\n            List<String> existingOutputs = pathNodes.get(inputType);\n            if (existingOutputs != null) {\n                outputs.addAll(existingOutputs);\n            }\n            pathNodes.put(inputType, outputs);\n        }\n\n        findAdditionalPossiblePaths(pathNodes, paths, \"\", rootType, maxDepth, \"\");\n\n        for (Entry<String, Set<String>> typeRemappingSet: typeRemappings.entrySet()) {\n            for (String typeRemapping: typeRemappingSet.getValue()) {\n\n                String remappedPath = typeRemapping + ':' + typeRemappingSet.getKey();\n                LOG.debug(\"Adding remapped path: {}\", remappedPath);\n                paths.add(remappedPath);\n                findAdditionalPossiblePaths(pathNodes, paths, typeRemappingSet.getKey(), typeRemapping, maxDepth - 1, \"\");\n            }\n        }\n\n        if (sortList) {\n            // If so desired sort the list by the name of the field (i.e. NOT the name of the type)\n            paths = paths\n                .stream()\n                .map(s -> s.split(\":\")).map(splits -> splits[1]+\":\"+splits[0]) // Swap type and field\n                .sorted(String::compareTo)                                     // Sort by field\n                .map(s -> s.split(\":\")).map(splits -> splits[1]+\":\"+splits[0]) // Swap type and field back to normal\n                .collect(Collectors.toList());\n        }\n\n        return paths;\n    }\n\n    /**\n     * Add all child paths in respect to the base (which is already present in the result set)\n     */\n    private void findAdditionalPossiblePaths(Map<String, List<String>> pathNodes,\n                                             List<String> paths,\n                                             String base,\n                                             String baseType,\n                                             int maxDepth,\n                                             String logPrefix) {\n        if (maxDepth == 0) {\n            return;\n        }\n\n        LOG.debug(\"Possible:{} > {}:{}\", logPrefix, baseType, base);\n\n        if (pathNodes.containsKey(baseType)) {\n            List<String> childPaths = pathNodes.get(baseType);\n            if (childPaths != null) {\n                for (String childPath : childPaths) {\n                    final int colonPos = childPath.indexOf(':');\n                    final String childType = childPath.substring(0, colonPos);\n                    final String childName = childPath.substring(colonPos + 1);\n\n                    String childBase;\n                    if (base.isEmpty()) {\n                        childBase = childName;\n                    } else {\n                        if (childName.isEmpty()) {\n                            childBase = base;\n                        } else {\n                            childBase = base + '.' + childName;\n                        }\n                    }\n\n                    String newPath = childType + ':' + childBase;\n                    if (!paths.contains(newPath)) {\n                        LOG.debug(\"Possible:{} + {}:{}\", logPrefix, childType, childBase);\n                        paths.add(childType + ':' + childBase);\n\n                        findAdditionalPossiblePaths(pathNodes, paths, childBase, childType, maxDepth - 1, logPrefix + \"--\");\n                    }\n                }\n            }\n        }\n        LOG.debug(\"Possible:{} < {}:{}\", logPrefix, baseType, base);\n    }\n\n    // --------------------------------------------\n\n}\n"
  },
  {
    "path": "parser-core/src/main/java/nl/basjes/parse/core/SimpleDissector.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.core;\n\nimport nl.basjes.parse.core.exceptions.DissectionFailure;\nimport nl.basjes.parse.core.exceptions.InvalidDissectorException;\n\nimport java.util.ArrayList;\nimport java.util.EnumSet;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport static nl.basjes.parse.core.Casts.NO_CASTS;\n\npublic abstract class SimpleDissector extends Dissector {\n\n    String inputType;\n    // Using HashMap instead of Map<> because a Map<> is not Serializable\n    private HashMap<String, EnumSet<Casts>> outputTypes;\n\n    private HashMap<String, EnumSet<Casts>> outputCasts;\n\n    public SimpleDissector(String inputType, Map<String, EnumSet<Casts>> outputTypes) {\n        this.inputType = inputType;\n        this.outputTypes = new HashMap<>(outputTypes);\n\n        outputCasts = new HashMap<>(outputTypes.size());\n        for (Map.Entry<String, EnumSet<Casts>> type: outputTypes.entrySet()) {\n            outputCasts.put(type.getKey().split(\":\", 2)[1], type.getValue());\n        }\n    }\n\n    @Override\n    public String getInputType() {\n        return inputType;\n    }\n\n    @Override\n    public void setInputType(String nInputType) {\n        inputType = nInputType;\n    }\n\n    @Override\n    public List<String> getPossibleOutput() {\n        return new ArrayList<>(outputTypes.keySet().stream().sorted().toList());\n    }\n\n    @Override\n    public EnumSet<Casts> prepareForDissect(String inputname, String outputname) {\n        String name = extractFieldName(inputname, outputname);\n        return outputCasts.getOrDefault(name, NO_CASTS);\n    }\n\n    @Override\n    protected void initializeNewInstance(Dissector newInstance) throws InvalidDissectorException {\n        if (newInstance instanceof SimpleDissector) {\n            SimpleDissector dissector = (SimpleDissector) newInstance;\n            dissector.inputType     = inputType;\n            dissector.outputTypes   = outputTypes;\n            dissector.outputCasts   = outputCasts;\n        }\n    }\n\n    @Override\n    public final void dissect(Parsable<?> parsable, String inputname) throws DissectionFailure {\n        final ParsedField field = parsable.getParsableField(getInputType(), inputname);\n        Value value = field.getValue();\n        if (value == null) {\n            return; // Nothing to do here\n        }\n        dissect(parsable, inputname, value);\n    }\n\n    public abstract void dissect(Parsable<?> parsable, String inputname, Value value) throws DissectionFailure;\n\n}\n"
  },
  {
    "path": "parser-core/src/main/java/nl/basjes/parse/core/Value.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage nl.basjes.parse.core;\n\npublic class Value {\n\n    enum Filled {\n        STRING,\n        LONG,\n        DOUBLE\n    }\n\n    private final Filled filled;\n    private String s = null;\n    private Long l = null;\n    private Double d = null;\n\n    public Value(String p) {\n        filled = Filled.STRING;\n        this.s = p;\n    }\n\n    public Value(Long p) {\n        filled = Filled.LONG;\n        this.l = p;\n    }\n\n    public Value(Double p) {\n        filled = Filled.DOUBLE;\n        this.d = p;\n    }\n\n    public String getString() {\n        switch (filled) {\n            case LONG:\n                return l == null ? null : Long.toString(l);\n            case DOUBLE:\n                return d == null ? null : Double.toString(d);\n            default: // == case STRING:\n                return s;\n        }\n    }\n\n    public Long getLong() {\n        switch (filled) {\n            case STRING:\n                try {\n                    return s == null ? null : Long.parseLong(s);\n                } catch (NumberFormatException e) {\n                    return null;\n                }\n            case DOUBLE:\n                return d == null ? null : (long) Math.floor(d + 0.5d); // Apply rounding\n            default: // == case LONG:\n                return l;\n        }\n    }\n\n    public Double getDouble() {\n        switch (filled) {\n            case STRING:\n                try {\n                    return s == null ? null : Double.parseDouble(s);\n                } catch (NumberFormatException e) {\n                    return null;\n                }\n            case LONG:\n                return l == null ? null : Double.valueOf(l);\n            default: // == case DOUBLE:\n                return d;\n        }\n    }\n\n    @Override\n    public String toString() {\n        StringBuilder sb = new StringBuilder();\n        sb  .append(\"Value{\")\n            .append(\"filled=\").append(filled);\n        if (s == null) {\n            sb.append(\", s=null\");\n        } else {\n            sb.append(\", s='\").append(s).append('\\'');\n        }\n        sb\n            .append(\", l=\").append(l)\n            .append(\", d=\").append(d)\n            .append('}');\n        return sb.toString();\n    }\n}\n"
  },
  {
    "path": "parser-core/src/main/java/nl/basjes/parse/core/exceptions/DissectionFailure.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.core.exceptions;\n\npublic class DissectionFailure extends Exception {\n    private static final long serialVersionUID = 1L;\n\n    public DissectionFailure(String message) {\n        super(message);\n    }\n    public DissectionFailure(String message, Throwable cause) {\n        super(message, cause);\n    }\n}\n"
  },
  {
    "path": "parser-core/src/main/java/nl/basjes/parse/core/exceptions/FatalErrorDuringCallOfSetterMethod.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.core.exceptions;\n\npublic class FatalErrorDuringCallOfSetterMethod extends RuntimeException {\n    private static final long serialVersionUID = 1L;\n\n    public FatalErrorDuringCallOfSetterMethod(String message){\n        super(\"Error occurred during setter call: \"+ message);\n    }\n\n    public FatalErrorDuringCallOfSetterMethod(String message, Throwable cause){\n        super(\"Error occurred during setter call: \"+ message, cause);\n    }\n\n}\n"
  },
  {
    "path": "parser-core/src/main/java/nl/basjes/parse/core/exceptions/InvalidDissectorException.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.core.exceptions;\n\npublic class InvalidDissectorException extends Exception {\n    private static final long serialVersionUID = 1L;\n\n    public InvalidDissectorException() {\n        super(\"Something went very wrong in constructing the dissector.\");\n    }\n\n    public InvalidDissectorException(String message) {\n        super(message);\n    }\n\n    public InvalidDissectorException(String message, Throwable cause) {\n        super(message, cause);\n    }\n\n}\n"
  },
  {
    "path": "parser-core/src/main/java/nl/basjes/parse/core/exceptions/InvalidFieldMethodSignature.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.core.exceptions;\n\nimport java.lang.reflect.Method;\n\npublic class InvalidFieldMethodSignature extends RuntimeException {\n    private static final long serialVersionUID = 1L;\n\n    public InvalidFieldMethodSignature(final Method method) {\n        super(\"The method \" + method.getDeclaringClass().getName() + \".\" + method.getName()\n                + \" does not conform to \\\"\" + method.getName() + \"(String name, String value)\\\".\");\n    }\n}\n"
  },
  {
    "path": "parser-core/src/main/java/nl/basjes/parse/core/exceptions/MissingDissectorsException.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.core.exceptions;\n\npublic class MissingDissectorsException extends Exception {\n    private static final long serialVersionUID = 1L;\n\n    public MissingDissectorsException(String message) {\n        super(message);\n    }\n}\n"
  },
  {
    "path": "parser-core/src/test/java/nl/basjes/parse/core/ParserCastsTest.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.core;\n\nimport nl.basjes.parse.core.exceptions.DissectionFailure;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.ArrayList;\nimport java.util.EnumSet;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport static nl.basjes.parse.core.Casts.DOUBLE_ONLY;\nimport static nl.basjes.parse.core.Casts.LONG_ONLY;\nimport static nl.basjes.parse.core.Casts.NO_CASTS;\nimport static nl.basjes.parse.core.Casts.STRING_ONLY;\nimport static nl.basjes.parse.core.Casts.STRING_OR_DOUBLE;\nimport static nl.basjes.parse.core.Casts.STRING_OR_LONG;\nimport static nl.basjes.parse.core.Casts.STRING_OR_LONG_OR_DOUBLE;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.fail;\n\nclass ParserCastsTest {\n\n    public static class MyDissector extends Dissector {\n\n        public MyDissector() {\n            // Empty\n        }\n\n        @Override\n        public void dissect(Parsable<?> parsable, final String inputname) throws DissectionFailure {\n            parsable.addDissection(inputname, \"OUTPUT_TYPE\", \"string_null\", (String)null);\n            parsable.addDissection(inputname, \"OUTPUT_TYPE\", \"string_good\", \"123\");\n\n            parsable.addDissection(inputname, \"OUTPUT_TYPE\", \"long_null\", (String)null);\n            parsable.addDissection(inputname, \"OUTPUT_TYPE\", \"long_bad\", \"Something\");\n            parsable.addDissection(inputname, \"OUTPUT_TYPE\", \"long_good\", \"123\");\n\n            parsable.addDissection(inputname, \"OUTPUT_TYPE\", \"double_null\", (String)null);\n            parsable.addDissection(inputname, \"OUTPUT_TYPE\", \"double_bad\", \"Something\");\n            parsable.addDissection(inputname, \"OUTPUT_TYPE\", \"double_good\", \"123\");\n\n            parsable.addDissection(inputname, \"OUTPUT_TYPE\", \"string_long_null\", (String)null);\n            parsable.addDissection(inputname, \"OUTPUT_TYPE\", \"string_double_null\", (String)null);\n            parsable.addDissection(inputname, \"OUTPUT_TYPE\", \"multi_null\", (String)null);\n            parsable.addDissection(inputname, \"OUTPUT_TYPE\", \"string_long_good\", \"123\");\n            parsable.addDissection(inputname, \"OUTPUT_TYPE\", \"string_double_good\", \"123\");\n            parsable.addDissection(inputname, \"OUTPUT_TYPE\", \"multi_good\", \"123\");\n\n        }\n\n        @Override\n        public String getInputType() {\n            return \"INPUT_TYPE\";\n        }\n\n        @Override\n        public List<String> getPossibleOutput() {\n            List<String> result = new ArrayList<>();\n            result.add(\"OUTPUT_TYPE:string_null\");\n            result.add(\"OUTPUT_TYPE:string_good\");\n            result.add(\"OUTPUT_TYPE:long_null\");\n            result.add(\"OUTPUT_TYPE:long_bad\");\n            result.add(\"OUTPUT_TYPE:long_good\");\n            result.add(\"OUTPUT_TYPE:double_null\");\n            result.add(\"OUTPUT_TYPE:double_bad\");\n            result.add(\"OUTPUT_TYPE:double_good\");\n            result.add(\"OUTPUT_TYPE:string_long_null\");\n            result.add(\"OUTPUT_TYPE:string_double_null\");\n            result.add(\"OUTPUT_TYPE:multi_null\");\n            result.add(\"OUTPUT_TYPE:string_long_good\");\n            result.add(\"OUTPUT_TYPE:string_double_good\");\n            result.add(\"OUTPUT_TYPE:multi_good\");\n            return result;\n        }\n\n\n        private static final Map<String, EnumSet<Casts>> PREPARE_FOR_DISSECT_MAP = new HashMap<>();\n        static {\n            PREPARE_FOR_DISSECT_MAP.put(\"string_null\",          STRING_ONLY);\n            PREPARE_FOR_DISSECT_MAP.put(\"string_good\",          STRING_ONLY);\n\n            PREPARE_FOR_DISSECT_MAP.put(\"long_null\",            LONG_ONLY);\n            PREPARE_FOR_DISSECT_MAP.put(\"long_bad\",             LONG_ONLY);\n            PREPARE_FOR_DISSECT_MAP.put(\"long_good\",            LONG_ONLY);\n\n            PREPARE_FOR_DISSECT_MAP.put(\"double_null\",          DOUBLE_ONLY);\n            PREPARE_FOR_DISSECT_MAP.put(\"double_bad\",           DOUBLE_ONLY);\n            PREPARE_FOR_DISSECT_MAP.put(\"double_good\",          DOUBLE_ONLY);\n            PREPARE_FOR_DISSECT_MAP.put(\"string_long_null\",     STRING_OR_LONG);\n            PREPARE_FOR_DISSECT_MAP.put(\"string_double_null\",   STRING_OR_DOUBLE);\n            PREPARE_FOR_DISSECT_MAP.put(\"multi_null\",           STRING_OR_LONG_OR_DOUBLE);\n            PREPARE_FOR_DISSECT_MAP.put(\"string_long_good\",     STRING_OR_LONG);\n            PREPARE_FOR_DISSECT_MAP.put(\"string_double_good\",   STRING_OR_DOUBLE);\n            PREPARE_FOR_DISSECT_MAP.put(\"multi_good\",           STRING_OR_LONG_OR_DOUBLE);\n        }\n\n        @Override\n        public EnumSet<Casts> prepareForDissect(String inputname, String outputname) {\n            return PREPARE_FOR_DISSECT_MAP.getOrDefault(outputname, NO_CASTS);\n        }\n    }\n\n    public static class MyParser<RECORD> extends Parser<RECORD> {\n        public MyParser(final Class<RECORD> clazz) {\n            super(clazz);\n            addDissector(new MyDissector());\n            setRootType(\"INPUT_TYPE\");\n        }\n    }\n\n    public static class MyRecord {\n        private int count = 0;\n        @Field({\"OUTPUT_TYPE:string_null\",\n                \"OUTPUT_TYPE:string_long_null\",\n                \"OUTPUT_TYPE:string_double_null\",\n                \"OUTPUT_TYPE:multi_null\"})\n        public void setStringNull(String value) {\n            assertEquals(null, value);\n            count++;\n        }\n\n        @Field({\"OUTPUT_TYPE:string_good\",\n                \"OUTPUT_TYPE:string_long_good\",\n                \"OUTPUT_TYPE:string_double_good\",\n                \"OUTPUT_TYPE:multi_good\"})\n        public void setStringGood(String value) {\n            assertEquals(\"123\", value);\n            count++;\n        }\n\n        @Field({\"OUTPUT_TYPE:long_null\",\n                \"OUTPUT_TYPE:long_bad\",\n                \"OUTPUT_TYPE:string_long_null\",\n                \"OUTPUT_TYPE:multi_null\"})\n        public void setLongNull(Long value) {\n            assertEquals(null, value);\n            count++;\n        }\n\n        @Field({\"OUTPUT_TYPE:long_good\",\n                \"OUTPUT_TYPE:string_long_good\",\n                \"OUTPUT_TYPE:multi_good\"})\n        public void setLongGood(Long value) {\n            assertEquals(Long.valueOf(123L), value);\n            count++;\n        }\n\n        @Field({\"OUTPUT_TYPE:double_null\",\n                \"OUTPUT_TYPE:double_bad\",\n                \"OUTPUT_TYPE:string_double_null\",\n                \"OUTPUT_TYPE:multi_null\"})\n        public void setDoubleNull(Double value) {\n            assertEquals(null, value);\n            count++;\n        }\n\n        @Field({\"OUTPUT_TYPE:double_good\",\n                \"OUTPUT_TYPE:string_double_good\",\n                \"OUTPUT_TYPE:multi_good\"})\n        public void setDoubleGood(Double value) {\n            assertEquals(123D, value, 0.0001D);\n            count++;\n        }\n\n        @SuppressWarnings(\"UnusedParameters\")\n        @Field({\"OUTPUT_TYPE:long_null\",\n                \"OUTPUT_TYPE:long_bad\",\n                \"OUTPUT_TYPE:long_good\",\n                \"OUTPUT_TYPE:string_long_null\",\n                \"OUTPUT_TYPE:string_long_good\"})\n        public void setLongWrongSignature(String name, Double value) {\n            fail(\"This setter uses Double but that is not allowed for \\\"\"+name+\"\\\" \");\n        }\n\n        @SuppressWarnings(\"UnusedParameters\")\n        @Field({\"OUTPUT_TYPE:double_null\",\n                \"OUTPUT_TYPE:double_bad\",\n                \"OUTPUT_TYPE:double_good\",\n                \"OUTPUT_TYPE:string_double_null\",\n                \"OUTPUT_TYPE:string_double_good\"})\n        public void setDoubleWrongSignature(String name, Long value) {\n            fail(\"This setter uses Long but that is not allowed for \\\"\"+name+\"\\\" \");\n        }\n    }\n\n    @Test\n    void testValidCasting() throws Exception {\n        Parser<MyRecord> parser = new MyParser<>(MyRecord.class);\n        MyRecord output = new MyRecord();\n        parser.parse(output, \"Something\");\n        assertEquals(22, output.count);\n\n        Map<String, EnumSet<Casts>> allCasts = parser.getAllCasts();\n        assertEquals(STRING_ONLY,               allCasts.get(\"OUTPUT_TYPE:string_good\"));\n        assertEquals(LONG_ONLY,                 allCasts.get(\"OUTPUT_TYPE:long_good\"));\n        assertEquals(DOUBLE_ONLY,               allCasts.get(\"OUTPUT_TYPE:double_good\"));\n        assertEquals(STRING_OR_LONG,            allCasts.get(\"OUTPUT_TYPE:string_long_good\"));\n        assertEquals(STRING_OR_DOUBLE,          allCasts.get(\"OUTPUT_TYPE:string_double_good\"));\n        assertEquals(STRING_OR_LONG_OR_DOUBLE,  allCasts.get(\"OUTPUT_TYPE:multi_good\"));\n\n        assertEquals(STRING_ONLY,               parser.getCasts(\"OUTPUT_TYPE:string_good\"));\n        assertEquals(LONG_ONLY,                 parser.getCasts(\"OUTPUT_TYPE:long_good\"));\n        assertEquals(DOUBLE_ONLY,               parser.getCasts(\"OUTPUT_TYPE:double_good\"));\n        assertEquals(STRING_OR_LONG,            parser.getCasts(\"OUTPUT_TYPE:string_long_good\"));\n        assertEquals(STRING_OR_DOUBLE,          parser.getCasts(\"OUTPUT_TYPE:string_double_good\"));\n        assertEquals(STRING_OR_LONG_OR_DOUBLE,  parser.getCasts(\"OUTPUT_TYPE:multi_good\"));\n    }\n\n}\n"
  },
  {
    "path": "parser-core/src/test/java/nl/basjes/parse/core/ParserDissectionOutputTypesTest.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.core;\n\nimport nl.basjes.parse.core.exceptions.DissectionFailure;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.ArrayList;\nimport java.util.EnumSet;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport static nl.basjes.parse.core.Casts.NO_CASTS;\nimport static nl.basjes.parse.core.Casts.STRING_ONLY;\nimport static nl.basjes.parse.core.Casts.STRING_OR_LONG;\nimport static nl.basjes.parse.core.Casts.STRING_OR_LONG_OR_DOUBLE;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\nclass ParserDissectionOutputTypesTest {\n\n    public static class TestDissector extends Dissector {\n\n        public TestDissector() {\n            // Empty\n        }\n\n        @Override\n        public void dissect(Parsable<?> parsable, final String inputname) throws DissectionFailure {\n            parsable.addDissection(inputname, \"OUTPUT_TYPE\", \"string_set_null\", (String) null);\n            parsable.addDissection(inputname, \"OUTPUT_TYPE\", \"string_set_string\", \"42\");\n\n            parsable.addDissection(inputname, \"OUTPUT_TYPE\", \"long_set_string_null\", (String) null);\n            parsable.addDissection(inputname, \"OUTPUT_TYPE\", \"long_set_string\", \"42\");\n\n            parsable.addDissection(inputname, \"OUTPUT_TYPE\", \"long_set_longclass_null\", (Long) null);\n            parsable.addDissection(inputname, \"OUTPUT_TYPE\", \"long_set_longclass\", Long.valueOf(42));\n            parsable.addDissection(inputname, \"OUTPUT_TYPE\", \"long_set_longprimitive\", 42L);\n\n            parsable.addDissection(inputname, \"OUTPUT_TYPE\", \"double_set_string_null\", (String) null);\n            parsable.addDissection(inputname, \"OUTPUT_TYPE\", \"double_set_string\", \"42\");\n\n            parsable.addDissection(inputname, \"OUTPUT_TYPE\", \"double_set_longclass_null\", (Long) null);\n            parsable.addDissection(inputname, \"OUTPUT_TYPE\", \"double_set_longclass\", Long.valueOf(42));\n            parsable.addDissection(inputname, \"OUTPUT_TYPE\", \"double_set_longprimitive\", 42L);\n\n            parsable.addDissection(inputname, \"OUTPUT_TYPE\", \"double_set_doubleclass_null\", (Double) null);\n            parsable.addDissection(inputname, \"OUTPUT_TYPE\", \"double_set_doubleclass\", Double.valueOf(42));\n            parsable.addDissection(inputname, \"OUTPUT_TYPE\", \"double_set_doubleprimitive\", 42D);\n\n        }\n\n        @Override\n        public String getInputType() {\n            return \"INPUT_TYPE\";\n        }\n\n        @Override\n        public List<String> getPossibleOutput() {\n            List<String> result = new ArrayList<>();\n            result.add(\"OUTPUT_TYPE:string_set_null\");\n            result.add(\"OUTPUT_TYPE:string_set_string\");\n\n            result.add(\"OUTPUT_TYPE:long_set_longclass_null\");\n            result.add(\"OUTPUT_TYPE:long_set_longclass\");\n            result.add(\"OUTPUT_TYPE:long_set_longprimitive\");\n            result.add(\"OUTPUT_TYPE:long_set_string_null\");\n            result.add(\"OUTPUT_TYPE:long_set_string\");\n\n            result.add(\"OUTPUT_TYPE:double_set_doubleclass_null\");\n            result.add(\"OUTPUT_TYPE:double_set_doubleclass\");\n            result.add(\"OUTPUT_TYPE:double_set_doubleprimitive\");\n            result.add(\"OUTPUT_TYPE:double_set_longclass_null\");\n            result.add(\"OUTPUT_TYPE:double_set_longclass\");\n            result.add(\"OUTPUT_TYPE:double_set_longprimitive\");\n            result.add(\"OUTPUT_TYPE:double_set_string_null\");\n            result.add(\"OUTPUT_TYPE:double_set_string\");\n            return result;\n        }\n\n        private static final Map<String, EnumSet<Casts>> PREPARE_FOR_DISSECT_MAP = new HashMap<>();\n\n        static {\n            PREPARE_FOR_DISSECT_MAP.put(\"string_set_null\",              STRING_ONLY);\n            PREPARE_FOR_DISSECT_MAP.put(\"string_set_string\",            STRING_ONLY);\n\n            PREPARE_FOR_DISSECT_MAP.put(\"long_set_longclass_null\",      STRING_OR_LONG);\n            PREPARE_FOR_DISSECT_MAP.put(\"long_set_longclass\",           STRING_OR_LONG);\n            PREPARE_FOR_DISSECT_MAP.put(\"long_set_longprimitive\",       STRING_OR_LONG);\n            PREPARE_FOR_DISSECT_MAP.put(\"long_set_string_null\",         STRING_OR_LONG);\n            PREPARE_FOR_DISSECT_MAP.put(\"long_set_string\",              STRING_OR_LONG);\n\n            PREPARE_FOR_DISSECT_MAP.put(\"double_set_doubleclass_null\",  STRING_OR_LONG_OR_DOUBLE);\n            PREPARE_FOR_DISSECT_MAP.put(\"double_set_doubleclass\",       STRING_OR_LONG_OR_DOUBLE);\n            PREPARE_FOR_DISSECT_MAP.put(\"double_set_doubleprimitive\",   STRING_OR_LONG_OR_DOUBLE);\n            PREPARE_FOR_DISSECT_MAP.put(\"double_set_longclass_null\",    STRING_OR_LONG_OR_DOUBLE);\n            PREPARE_FOR_DISSECT_MAP.put(\"double_set_longclass\",         STRING_OR_LONG_OR_DOUBLE);\n            PREPARE_FOR_DISSECT_MAP.put(\"double_set_longprimitive\",     STRING_OR_LONG_OR_DOUBLE);\n            PREPARE_FOR_DISSECT_MAP.put(\"double_set_string_null\",       STRING_OR_LONG_OR_DOUBLE);\n            PREPARE_FOR_DISSECT_MAP.put(\"double_set_string\",            STRING_OR_LONG_OR_DOUBLE);\n        }\n\n        @Override\n        public EnumSet<Casts> prepareForDissect(String inputname, String outputname) {\n            return PREPARE_FOR_DISSECT_MAP.getOrDefault(outputname, NO_CASTS);\n        }\n    }\n\n    public static class TestParser<RECORD> extends Parser<RECORD> {\n        TestParser(final Class<RECORD> clazz) {\n            super(clazz);\n            addDissector(new TestDissector());\n            setRootType(\"INPUT_TYPE\");\n        }\n    }\n\n    @SuppressWarnings(\"unused\")\n    public static class TestRecord {\n        private int count = 0;\n\n        @Field({\n            \"OUTPUT_TYPE:string_set_null\",\n            \"OUTPUT_TYPE:long_set_longclass_null\",\n            \"OUTPUT_TYPE:long_set_string_null\",\n            \"OUTPUT_TYPE:double_set_doubleclass_null\",\n            \"OUTPUT_TYPE:double_set_longclass_null\",\n            \"OUTPUT_TYPE:double_set_string_null\"\n        })\n        public void setStringNull(String value) {\n            count++;\n            assertEquals(null, value);\n        }\n\n        @Field({\n            \"OUTPUT_TYPE:long_set_longclass_null\",\n            \"OUTPUT_TYPE:long_set_string_null\",\n            \"OUTPUT_TYPE:double_set_doubleclass_null\",\n            \"OUTPUT_TYPE:double_set_longclass_null\",\n            \"OUTPUT_TYPE:double_set_string_null\"\n        })\n        public void setLongNull(Long value) {\n            count++;\n            assertEquals(null, value);\n        }\n\n        @Field({\n            \"OUTPUT_TYPE:double_set_doubleclass_null\",\n            \"OUTPUT_TYPE:double_set_longclass_null\",\n            \"OUTPUT_TYPE:double_set_string_null\"\n        })\n        public void setDoubleNull(Double value) {\n            count++;\n            assertEquals(null, value);\n        }\n\n        @Field({\n            \"OUTPUT_TYPE:string_set_string\",\n            \"OUTPUT_TYPE:long_set_longclass\",\n            \"OUTPUT_TYPE:long_set_longprimitive\",\n            \"OUTPUT_TYPE:long_set_string\",\n            \"OUTPUT_TYPE:double_set_longclass\",\n            \"OUTPUT_TYPE:double_set_longprimitive\",\n            \"OUTPUT_TYPE:double_set_string\"\n        })\n        public void setString(String value) {\n            count++;\n            assertEquals(\"42\", value);\n        }\n\n        @Field({\n            \"OUTPUT_TYPE:double_set_doubleclass\",\n            \"OUTPUT_TYPE:double_set_doubleprimitive\"\n        })\n        public void setStringFromDouble(String value) {\n            count++;\n            assertEquals(\"42.0\", value);\n        }\n\n        @Field({\n            \"OUTPUT_TYPE:long_set_longclass\",\n            \"OUTPUT_TYPE:long_set_longprimitive\",\n            \"OUTPUT_TYPE:long_set_string\",\n            \"OUTPUT_TYPE:double_set_doubleclass\",\n            \"OUTPUT_TYPE:double_set_doubleprimitive\",\n            \"OUTPUT_TYPE:double_set_longclass\",\n            \"OUTPUT_TYPE:double_set_longprimitive\",\n            \"OUTPUT_TYPE:double_set_string\"\n        })\n        public void setLong(Long value) {\n            count++;\n            assertEquals(Long.valueOf(42L), value);\n        }\n\n        @Field({\n            \"OUTPUT_TYPE:double_set_doubleclass\",\n            \"OUTPUT_TYPE:double_set_doubleprimitive\",\n            \"OUTPUT_TYPE:double_set_longclass\",\n            \"OUTPUT_TYPE:double_set_longprimitive\",\n            \"OUTPUT_TYPE:double_set_string\"\n        })\n        public void setDouble(Double value) {\n            count++;\n            assertEquals(42D, value, 0.01);\n        }\n    }\n\n    @Test\n    void testSetterTypes() throws Exception {\n        Parser<TestRecord> parser = new TestParser<>(TestRecord.class);\n        TestRecord output = new TestRecord();\n        parser.parse(output, \"Something\");\n        assertEquals(36, output.count);\n    }\n\n}\n"
  },
  {
    "path": "parser-core/src/test/java/nl/basjes/parse/core/ParserDuplicateOutputTest.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.core;\n\nimport nl.basjes.parse.core.exceptions.DissectionFailure;\nimport nl.basjes.parse.core.test.DissectorTester;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.EnumSet;\nimport java.util.HashMap;\n\nimport static nl.basjes.parse.core.Casts.STRING_ONLY;\n\nclass ParserDuplicateOutputTest {\n\n    public abstract static class MyDissector extends SimpleDissector {\n        private static final HashMap<String, EnumSet<Casts>> DISSECTOR_CONFIG = new HashMap<>();\n        static {\n            DISSECTOR_CONFIG.put(\"STRING:output\",   STRING_ONLY);\n        }\n\n        public MyDissector() {\n            super(\"INPUT\", DISSECTOR_CONFIG);\n        }\n    }\n\n    public static class FooDissector extends MyDissector {\n        @Override\n        public void dissect(Parsable<?> parsable, String inputname, Value value) throws DissectionFailure {\n            parsable.addDissection(inputname, \"STRING\", \"output\", \"foo\");\n        }\n    }\n    public static class BarDissector extends MyDissector {\n        @Override\n        public void dissect(Parsable<?> parsable, String inputname, Value value) throws DissectionFailure {\n            parsable.addDissection(inputname, \"STRING\", \"output\", \"bar\");\n        }\n    }\n\n    // Verify: If you have two dissectors doing the SAME input/output you should get BOTH\n    @Test\n    void testParseString() {\n        DissectorTester.create()\n            .verbose()\n            .withDissector(new FooDissector())\n            .withDissector(new BarDissector())\n            .withInput(\"SomeThing\")\n            .printPossible()\n            .expect(\"STRING:output\", \"foo\")\n            .expect(\"STRING:output\", \"bar\")\n            .checkExpectations();\n    }\n}\n"
  },
  {
    "path": "parser-core/src/test/java/nl/basjes/parse/core/ParserExceptionsTest.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.core;\n\nimport nl.basjes.parse.core.exceptions.DissectionFailure;\nimport nl.basjes.parse.core.exceptions.InvalidDissectorException;\nimport nl.basjes.parse.core.exceptions.InvalidFieldMethodSignature;\nimport nl.basjes.parse.core.exceptions.MissingDissectorsException;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.EnumSet;\nimport java.util.List;\n\nimport static nl.basjes.parse.core.Casts.STRING_ONLY;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\nclass ParserExceptionsTest {\n\n    public static class TestDissector extends Dissector {\n        private String inputType;\n        private String outputType;\n        private String outputName;\n\n        public TestDissector(String inputType, String outputType, String outputName) {\n            init(inputType, outputType, outputName);\n        }\n\n        public final void init(String inputtype, String outputtype, String outputname) {\n            this.inputType  = inputtype;\n            this.outputType = outputtype;\n            this.outputName = outputname;\n        }\n\n        protected void initializeNewInstance(Dissector newInstance) {\n            ((TestDissector)newInstance).init(inputType, outputType, outputName);\n        }\n\n        @Override\n        public void dissect(Parsable<?> parsable, final String inputname) throws DissectionFailure {\n            final ParsedField field = parsable.getParsableField(inputType, inputname);\n            parsable.addDissection(inputname, outputType, outputName, field.getValue());\n        }\n\n        @Override\n        public String getInputType() {\n            return inputType;\n        }\n\n        @Override\n        public List<String> getPossibleOutput() {\n            List<String> result = new ArrayList<>();\n            result.add(outputType + \":\" + outputName);\n            return result;\n        }\n\n        @Override\n        public EnumSet<Casts> prepareForDissect(String inputname, String outputname) {\n            return STRING_ONLY;\n        }\n    }\n\n    public static class TestDissectorOne extends TestDissector {\n        public TestDissectorOne() {\n            super(\"INPUTTYPE\", \"SOMETYPE\", \"output1\");\n        }\n    }\n\n    public static class TestDissectorTwo extends TestDissector {\n        public TestDissectorTwo() {\n            super(\"INPUTTYPE\", \"OTHERTYPE\", \"output2\");\n        }\n    }\n\n    public static class TestDissectorThree extends TestDissector {\n        public TestDissectorThree() {\n            super(\"SOMETYPE\", \"FOO\", \"foo\");\n        }\n    }\n\n    public static class TestDissectorFour extends TestDissector {\n        public TestDissectorFour() {\n            super(\"SOMETYPE\", \"BAR\", \"bar\");\n        }\n    }\n\n    public static class TestParser<RECORD> extends Parser<RECORD> {\n        public TestParser(final Class<RECORD> clazz) {\n            super(clazz);\n            addDissector(new TestDissectorOne());\n            addDissector(new TestDissectorTwo());\n\n            // Needlessly clumsy to test the addDissectors method too.\n            List<Dissector> dissectors = new ArrayList<>();\n            dissectors.add(new TestDissectorThree());\n            dissectors.add(new TestDissectorFour());\n            addDissectors(dissectors);\n            setRootType(\"INPUTTYPE\");\n        }\n    }\n\n    @SuppressWarnings(\"unused\")\n    public static class TestRecord {\n        private String output1 = \"xxx\";\n        @Field(\"SOMETYPE:output1\")\n        public void setValue1(String value) {\n            output1 = \"SOMETYPE1:SOMETYPE:output1:\" + value;\n        }\n\n        private String output2 = \"yyy\";\n//        @Field(\"OTHERTYPE:output\") --> Set via direct method\n        public void setValue2(String name, String value) {\n            output2 = \"OTHERTYPE2:\" + name + \":\" + value;\n        }\n\n        private String output3a = \"xxx\";\n        private String output3b = \"yyy\";\n\n        @Field({ \"SOMETYPE:output1\", \"OTHERTYPE:output2\" })\n        public void setValue3(String name, String value) {\n            if (name.startsWith(\"SOMETYPE:\")) {\n                output3a = \"SOMETYPE3:\" + name + \":\" + value;\n            } else {\n                output3b = \"OTHERTYPE3:\" + name + \":\" + value;\n            }\n        }\n\n        private String output4a = \"X\";\n        private String output4b = \"Y\";\n\n        @Field({ \"SOMETYPE:output1\", \"OTHERTYPE:output2\", \"SOMETYPE:output1\", \"OTHERTYPE:output2\" })\n        public void setValue4(String name, String value) {\n            if (name.startsWith(\"SOMETYPE:\")) {\n                output4a = output4a + \"=SOMETYPE:\" + name + \":\" + value;\n            } else {\n                output4b = output4b + \"=OTHERTYPE:\" + name + \":\" + value;\n            }\n        }\n\n        private String output5a = \"X\";\n        private String output5b = \"Y\";\n\n        @Field({ \"SOMETYPE:output1\", \"OTHERTYPE:output2\", \"SOMETYPE:*\", \"OTHERTYPE:*\" })\n        public void setValue5(String name, String value) {\n            if (name.startsWith(\"SOMETYPE:\")) {\n                output5a = output5a + \"=SOMETYPE:\" + name + \":\" + value;\n            } else {\n                output5b = output5b + \"=OTHERTYPE:\" + name + \":\" + value;\n            }\n        }\n\n        private String output6 = \"Z\";\n        @Field({ \"FOO:output1.foo\"})\n        public void setValue6(String name, String value) {\n            output6 = output6 + \"=FOO:\" + name + \":\" + value;\n        }\n\n        private String output7 = \"Z\";\n        @Field({ \"BAR:output1.bar\"})\n        public void setValue7(String name, String value) {\n            output7 = output7 + \"=BAR:\" + name + \":\" + value;\n        }\n\n        private String output8 = \"Z\";\n        public void setValue8(String name, String value) {\n            output8 = output8 + \"=\" + name + \":\" + value;\n        }\n\n        @SuppressWarnings({\"UnusedDeclaration\", \"EmptyMethod\"})\n        public void badSetter1() {\n        }\n\n        @SuppressWarnings({\"UnusedDeclaration\", \"EmptyMethod\"})\n        public void badSetter2(String name, Float value) {\n        }\n    }\n\n    @Test\n    void testParseString() throws Exception {\n        Parser<TestRecord> parser = new TestParser<>(TestRecord.class);\n\n        String[] params = {\"OTHERTYPE:output2\"};\n        parser.addParseTarget(TestRecord.class.getMethod(\"setValue2\", String.class, String.class), Arrays.asList(params));\n\n        TestRecord output = new TestRecord();\n        parser.parse(output, \"Something\");\n        assertEquals(\"SOMETYPE1:SOMETYPE:output1:Something\", output.output1);\n        assertEquals(\"OTHERTYPE2:OTHERTYPE:output2:Something\", output.output2);\n        assertEquals(\"SOMETYPE3:SOMETYPE:output1:Something\", output.output3a);\n        assertEquals(\"OTHERTYPE3:OTHERTYPE:output2:Something\", output.output3b);\n        assertEquals(\"X=SOMETYPE:SOMETYPE:output1:Something\", output.output4a);\n        assertEquals(\"Y=OTHERTYPE:OTHERTYPE:output2:Something\", output.output4b);\n        assertEquals(\"X=SOMETYPE:SOMETYPE:output1:Something=SOMETYPE:SOMETYPE:output1:Something\", output.output5a);\n        assertEquals(\"Y=OTHERTYPE:OTHERTYPE:output2:Something=OTHERTYPE:OTHERTYPE:output2:Something\", output.output5b);\n        assertEquals(\"Z=FOO:FOO:output1.foo:Something\", output.output6);\n        assertEquals(\"Z=BAR:BAR:output1.bar:Something\", output.output7);\n    }\n\n    @Test\n    void testGetPossiblePaths() throws Exception {\n        Parser<TestRecord> parser = new TestParser<>(TestRecord.class);\n\n        String[] params = {\"OTHERTYPE:output2\"};\n        parser.addParseTarget(TestRecord.class.getMethod(\"setValue2\", String.class, String.class), Arrays.asList(params));\n\n        List<String> paths = parser.getPossiblePaths(3);\n        assertEquals(4, paths.size());\n        assertTrue(paths.contains(\"SOMETYPE:output1\"));\n        assertTrue(paths.contains(\"FOO:output1.foo\"));\n        assertTrue(paths.contains(\"BAR:output1.bar\"));\n        assertTrue(paths.contains(\"OTHERTYPE:output2\"));\n    }\n\n    @Test\n    void testBadSetter1() {\n        Parser<TestRecord> parser = new TestParser<>(TestRecord.class);\n\n        String[] params = {\"OTHERTYPE:output2\"};\n        assertThrows(InvalidFieldMethodSignature.class, () -> {\n            parser.addParseTarget(TestRecord.class.getMethod(\"badSetter1\"), Arrays.asList(params));\n        });\n    }\n\n    @Test\n    void testBadSetter2() {\n        Parser<TestRecord> parser = new TestParser<>(TestRecord.class);\n\n        String[] params = {\"OTHERTYPE:output2\"};\n        assertThrows(InvalidFieldMethodSignature.class, ()-> {\n            parser.addParseTarget(TestRecord.class.getMethod(\"badSetter2\", String.class, Float.class), Arrays.asList(params));\n        });\n    }\n\n    public static class BrokenTestDissector extends Dissector {\n\n        public BrokenTestDissector() {\n        }\n\n        @Override\n        public boolean initializeFromSettingsParameter(String settings) {\n            return true; // Everything went right\n        }\n\n        @Override\n        public void dissect(Parsable<?> parsable, String inputname) {\n        }\n\n        @Override\n        public String getInputType() {\n            return \"FOO\";\n        }\n\n        @Override\n        public List<String> getPossibleOutput() {\n            List<String> result = new ArrayList<>();\n            result.add(\"FOO:bar\");\n            return result;\n        }\n\n        @Override\n        public EnumSet<Casts> prepareForDissect(String inputname, String outputname) {\n            return STRING_ONLY;\n        }\n\n        @Override\n        public void prepareForRun() throws InvalidDissectorException {\n            throw new InvalidDissectorException();\n        }\n    }\n\n    @Test\n    void testBrokenDissector() throws Exception {\n        Parser<TestRecord> parser = new TestParser<>(TestRecord.class);\n        Dissector dissector = new BrokenTestDissector();\n        parser.setRootType(dissector.getInputType());\n        parser.addParseTarget(TestRecord.class.getMethod(\"setValue8\", String.class, String.class), \"FOO:bar\");\n        parser.addDissector(dissector);\n        assertThrows(InvalidDissectorException.class, ()-> {\n            parser.parse(\"Something\");\n        });\n    }\n\n    public static class BrokenTestDissector2 extends BrokenTestDissector {\n        // Non public constructor\n        BrokenTestDissector2() {\n        }\n    }\n\n    @Test\n    void testBrokenDissector2() throws Exception {\n        Parser<TestRecord> parser = new TestParser<>(TestRecord.class);\n        Dissector dissector = new BrokenTestDissector2();\n        parser.setRootType(dissector.getInputType());\n        parser.addParseTarget(TestRecord.class.getMethod(\"setValue8\", String.class, String.class), \"FOO:bar\");\n        parser.addDissector(dissector);\n        assertThrows(InvalidDissectorException.class, ()-> {\n            parser.parse(\"Something\");\n        });\n    }\n\n    @Test\n    void testChangeAfterStart() throws Exception {\n        Parser<TestRecord> parser = new TestParser<>(TestRecord.class);\n        parser.parse(\"Something\");\n        assertNotNull(parser.addDissector(new BrokenTestDissector()));\n    }\n\n    @Test\n    void testDropDissector1() {\n        Parser<TestRecord> parser = new TestParser<>(TestRecord.class);\n\n        parser.dropDissector(TestDissectorOne.class);\n        assertThrows(MissingDissectorsException.class, () -> {\n            parser.parse(\"Something\");\n        });\n    }\n\n    @Test\n    void testDropDissector2() {\n        Parser<TestRecord> parser = new TestParser<>(TestRecord.class);\n\n        parser.dropDissector(TestDissectorOne.class);\n        parser.addDissector(new TestDissectorOne());\n        assertNotNull(parser.getPossiblePaths());\n    }\n\n    @Test\n    void testDropDissector3() throws Exception {\n        Parser<TestRecord> parser = new TestParser<>(TestRecord.class);\n\n        parser.parse(\"Something\");\n        assertNotNull(parser.dropDissector(TestDissectorOne.class));\n    }\n\n}\n"
  },
  {
    "path": "parser-core/src/test/java/nl/basjes/parse/core/ParserInfiniteLoopTest.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.core;\n\nimport nl.basjes.parse.core.exceptions.DissectionFailure;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.ArrayList;\nimport java.util.EnumSet;\nimport java.util.List;\n\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\n\nclass ParserInfiniteLoopTest {\n\n    public static class TestDissector extends Dissector {\n\n        public TestDissector() {\n            // Empty\n        }\n\n        @Override\n        public void dissect(Parsable<?> parsable, final String inputname) throws DissectionFailure {\n            parsable.addDissection(inputname, \"OUTPUT_TYPE\", \"string\", \"123\");\n        }\n\n        @Override\n        public String getInputType() {\n            return \"INPUT_TYPE\";\n        }\n\n        @Override\n        public List<String> getPossibleOutput() {\n            List<String> result = new ArrayList<>();\n            result.add(\"OUTPUT_TYPE:string\");\n            return result;\n        }\n\n        @Override\n        public EnumSet<Casts> prepareForDissect(String inputname, String outputname) {\n            return Casts.STRING_ONLY;\n        }\n    }\n\n    public static class TestParser<RECORD> extends Parser<RECORD> {\n        public TestParser(final Class<RECORD> clazz) {\n            super(clazz);\n            addDissector(new TestDissector());\n            setRootType(\"INPUT_TYPE\");\n        }\n    }\n\n    public static class TestRecord {\n        @SuppressWarnings({\"EmptyMethod\", \"UnusedParameters\"})\n        @Field({\"OUTPUT_TYPE:string\"})\n        public void set(String name, String value) {\n            // Do nothing\n        }\n    }\n\n\n    /**\n     * This creates a dissector and a type remap that causes it to (effectively)\n     * be an infinite recursive loop. This is the reproduction situation for a problem\n     * that is now fixed.\n     * @throws Exception in case of error\n     */\n    @Test\n    void testInfiniteRecursionAvoidance() throws Exception {\n        Parser<TestRecord> parser = new TestParser<>(TestRecord.class);\n        parser.addTypeRemapping(\"string\", \"INPUT_TYPE\");\n        TestRecord output = new TestRecord();\n        assertNotNull(parser.parse(output, \"Something\"));\n        // If this works then it will cleanly end (instead of a stack overflow)\n    }\n\n}\n"
  },
  {
    "path": "parser-core/src/test/java/nl/basjes/parse/core/ParserNormalTest.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.core;\n\nimport nl.basjes.parse.core.exceptions.DissectionFailure;\nimport nl.basjes.parse.core.exceptions.InvalidDissectorException;\nimport nl.basjes.parse.core.exceptions.MissingDissectorsException;\nimport nl.basjes.parse.core.test.NormalValuesDissector;\nimport nl.basjes.parse.core.test.TestRecord;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.EnumSet;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport static nl.basjes.parse.core.Casts.STRING_ONLY;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.api.Assertions.fail;\n\nclass ParserNormalTest {\n\n    public static class MyDissector extends Dissector {\n        private String      inputType;\n        private String      outputType;\n        private String      outputName;\n        private final Set<String> outputNames = new HashSet<>();\n\n        public MyDissector(String inputType, String outputType, String outputName) {\n            this.inputType = inputType;\n            this.outputType = outputType;\n            this.outputName = outputName;\n            this.outputNames.add(outputName);\n        }\n\n        public void init(String inputtype, String outputtype, String outputname) {\n            this.inputType = inputtype;\n            this.outputType = outputtype;\n            this.outputName = outputname;\n            this.outputNames.add(outputname);\n        }\n\n        @Override\n        protected void initializeNewInstance(Dissector newInstance) {\n            ((MyDissector)newInstance).init(inputType, outputType, outputName);\n        }\n\n        @Override\n        public void dissect(Parsable<?> parsable, String inputname) throws DissectionFailure {\n            final ParsedField field = parsable.getParsableField(inputType, inputname);\n            for (String outputname : outputNames) {\n                parsable.addDissection(inputname, outputType, outputname, field.getValue());\n            }\n        }\n\n        @Override\n        public String getInputType() {\n            return inputType;\n        }\n\n        @Override\n        public List<String> getPossibleOutput() {\n            List<String> result = new ArrayList<>();\n            result.add(outputType + \":\" + outputName);\n            return result;\n        }\n\n        @Override\n        public EnumSet<Casts> prepareForDissect(String inputname, String outputname) {\n            String name = outputname;\n            String prefix = inputname + '.';\n            if (outputname.startsWith(prefix)) {\n                name = outputname.substring(prefix.length());\n            }\n            outputNames.add(name);\n            return STRING_ONLY;\n        }\n    }\n\n    public static class MyDissectorOne extends MyDissector {\n        public MyDissectorOne() {\n            super(\"INPUTTYPE\", \"SOMETYPE\", \"output1\");\n        }\n    }\n\n    public static class MyDissectorTwo extends MyDissector {\n        public MyDissectorTwo() {\n            super(\"INPUTTYPE\", \"OTHERTYPE\", \"output2\");\n        }\n    }\n\n    public static class MyDissectorThree extends MyDissector {\n        public MyDissectorThree() {\n            super(\"SOMETYPE\", \"FOO\", \"foo\");\n        }\n    }\n\n    public static class MyDissectorFour extends MyDissector {\n        public MyDissectorFour() {\n            super(\"SOMETYPE\", \"BAR\", \"bar\");\n        }\n    }\n\n    public static class MyDissectorWildCard extends MyDissector {\n        public MyDissectorWildCard() {\n            super(\"SOMETYPE\", \"WILD\", \"*\");\n        }\n\n    }\n\n    public static class TestParser<RECORD> extends Parser<RECORD> {\n        public TestParser(final Class<RECORD> clazz) {\n            super(clazz);\n            addDissector(new MyDissectorOne());\n            addDissector(new MyDissectorTwo());\n            addDissector(new MyDissectorThree());\n            addDissector(new MyDissectorFour());\n            addDissector(new MyDissectorWildCard());\n            setRootType(\"INPUTTYPE\");\n        }\n    }\n\n    @Test\n    void testParseString() throws Exception {\n        Parser<ParserNormalTestRecord> parser = new TestParser<>(ParserNormalTestRecord.class);\n\n        String[] params = {\"OTHERTYPE:output2\"};\n        parser\n            .addParseTarget(ParserNormalTestRecord.class.getMethod(\"setValue2\", String.class, String.class), Arrays.asList(params))\n            .dropDissector(MyDissectorWildCard.class)\n            .addDissector(new MyDissectorWildCard());\n\n        ParserNormalTestRecord output = new ParserNormalTestRecord();\n        parser.parse(output, \"Something\");\n        assertEquals(\"SOMETYPE1:SOMETYPE:output1:Something\", output.getOutput1());\n        assertEquals(\"OTHERTYPE2:OTHERTYPE:output2:Something\", output.getOutput2());\n        assertEquals(\"SOMETYPE3:SOMETYPE:output1:Something\", output.getOutput3a());\n        assertEquals(\"OTHERTYPE3:OTHERTYPE:output2:Something\", output.getOutput3b());\n        assertEquals(\"X=SOMETYPE:SOMETYPE:output1:Something\", output.getOutput4a());\n        assertEquals(\"Y=OTHERTYPE:OTHERTYPE:output2:Something\", output.getOutput4b());\n        assertEquals(\"X=SOMETYPE:SOMETYPE:output1:Something=SOMETYPE:SOMETYPE:output1:Something\", output.getOutput5a());\n        assertEquals(\"Y=OTHERTYPE:OTHERTYPE:output2:Something=OTHERTYPE:OTHERTYPE:output2:Something\", output.getOutput5b());\n        assertEquals(\"Z=FOO:FOO:output1.foo:Something\", output.getOutput6());\n        assertEquals(\"Z=BAR:BAR:output1.bar:Something\", output.getOutput7());\n        assertEquals(\"Z=WILD:WILD:output1.wild:Something\", output.getOutput8());\n    }\n\n    // ---------------------------------------------\n    @Test\n    void testParseStringInstantiate() throws Exception {\n        Parser<ParserNormalTestRecord> parser = new TestParser<>(ParserNormalTestRecord.class);\n\n        String[] params = {\"OTHERTYPE:output2\"};\n        parser.addParseTarget(ParserNormalTestRecord.class.getMethod(\"setValue2\", String.class, String.class), Arrays.asList(params));\n\n        ParserNormalTestRecord output = parser.parse(\"Something\");\n\n        assertEquals(\"SOMETYPE1:SOMETYPE:output1:Something\", output.getOutput1());\n        assertEquals(\"OTHERTYPE2:OTHERTYPE:output2:Something\", output.getOutput2());\n        assertEquals(\"SOMETYPE3:SOMETYPE:output1:Something\", output.getOutput3a());\n        assertEquals(\"OTHERTYPE3:OTHERTYPE:output2:Something\", output.getOutput3b());\n        assertEquals(\"X=SOMETYPE:SOMETYPE:output1:Something\", output.getOutput4a());\n        assertEquals(\"Y=OTHERTYPE:OTHERTYPE:output2:Something\", output.getOutput4b());\n        assertEquals(\"X=SOMETYPE:SOMETYPE:output1:Something=SOMETYPE:SOMETYPE:output1:Something\", output.getOutput5a());\n        assertEquals(\"Y=OTHERTYPE:OTHERTYPE:output2:Something=OTHERTYPE:OTHERTYPE:output2:Something\", output.getOutput5b());\n        assertEquals(\"Z=FOO:FOO:output1.foo:Something\", output.getOutput6());\n        assertEquals(\"Z=BAR:BAR:output1.bar:Something\", output.getOutput7());\n        assertEquals(\"Z=WILD:WILD:output1.wild:Something\", output.getOutput8());\n    }\n\n    // ---------------------------------------------\n\n    @Test\n    void testMissingDissector() {\n        Parser<ParserNormalTestRecord> parser = new TestParser<>(ParserNormalTestRecord.class);\n\n        // Cripple the parser\n        parser.dropDissector(MyDissectorTwo.class);\n\n        ParserNormalTestRecord output = new ParserNormalTestRecord();\n\n        assertThrows(MissingDissectorsException.class, () -> {\n            parser.parse(output, \"Something\"); // Should fail.\n        });\n    }\n\n    @Test\n    void testGetPossiblePaths() throws Exception {\n        Parser<ParserNormalTestRecord> parser = new TestParser<>(ParserNormalTestRecord.class);\n\n        String[] params = {\"OTHERTYPE:output2\"};\n        parser.addParseTarget(ParserNormalTestRecord.class.getMethod(\"setValue2\", String.class, String.class), Arrays.asList(params));\n\n        List<String> paths = parser.getPossiblePaths(3);\n        for (String path : paths) {\n            System.out.println(\"XXX \" + path);\n        }\n\n    }\n\n    @Test\n    void testAddTypeRemapping() throws NoSuchMethodException, InvalidDissectorException, MissingDissectorsException, DissectionFailure {\n        new Parser<>(TestRecord.class)\n            .setRootType(\"INPUT\")\n            .addDissector(new NormalValuesDissector())\n            .addTypeRemapping(\"string\", \"STRINGXX\")\n            .addParseTarget(\"setStringValue\", \"STRINGXX:string\")\n            .addTypeRemapping(\"string\", \"STRINGYY\")\n            .addParseTarget(\"setStringValue\", \"STRINGYY:string\")\n            .parse(\"Doesn't matter\")\n            .expectString(\"STRINGXX:string\", \"FortyTwo\")\n            .expectString(\"STRINGYY:string\", \"FortyTwo\");\n    }\n\n    @Test\n    void testAddTypeRemappings() throws NoSuchMethodException, InvalidDissectorException, MissingDissectorsException, DissectionFailure {\n        new Parser<>(TestRecord.class)\n            .setRootType(\"INPUT\")\n            .addDissector(new NormalValuesDissector())\n            .addTypeRemapping(\"string\", \"STRINGXX\")\n            .addParseTarget(\"setStringValue\", \"STRINGXX:string\")\n            .addTypeRemappings(Collections.singletonMap(\"string\", Collections.singleton(\"STRINGYY\")))\n            .addParseTarget(\"setStringValue\", \"STRINGYY:string\")\n            .parse(\"Doesn't matter\")\n            .expectString(\"STRINGXX:string\", \"FortyTwo\")\n            .expectString(\"STRINGYY:string\", \"FortyTwo\");\n    }\n\n    @Test\n    void testSetTypeRemapping() throws NoSuchMethodException, InvalidDissectorException, DissectionFailure {\n        try{\n            new Parser<>(TestRecord.class)\n                .setRootType(\"INPUT\")\n                .addDissector(new NormalValuesDissector())\n                .addTypeRemapping(\"string\", \"STRINGXX\")\n                .addParseTarget(\"setStringValue\", \"STRINGXX:string\")\n                // Should wipe previous\n                .setTypeRemappings(Collections.singletonMap(\"string\", Collections.singleton(\"STRINGYY\")))\n                .addParseTarget(\"setStringValue\", \"STRINGYY:string\")\n                .parse(\"Doesn't matter\");\n\n            fail(\"We should get an exception because the mapping to STRINGXX:string wass removed.\");\n        } catch (MissingDissectorsException mde) {\n            assertTrue(mde.getMessage().contains(\"STRINGXX:string\"));\n        }\n    }\n\n}\n"
  },
  {
    "path": "parser-core/src/test/java/nl/basjes/parse/core/ParserNormalTestRecord.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.core;\n\npublic class ParserNormalTestRecord {\n\n    public ParserNormalTestRecord() {\n        // Empty but needed for instantiating\n    }\n\n    private String output1 = \"xxx\";\n\n    @Field(\"SOMETYPE:output1\")\n    public void setValue1(String value) {\n        output1 = \"SOMETYPE1:SOMETYPE:output1:\" + value;\n    }\n\n    public String getOutput1() {\n        return output1;\n    }\n\n    private String output2 = \"yyy\";\n\n    // @Field(\"OTHERTYPE:output\") --> Set via direct method\n    public void setValue2(String name, String value) {\n        output2 = \"OTHERTYPE2:\" + name + \":\" + value;\n    }\n\n    public String getOutput2() {\n        return output2;\n    }\n\n    private String output3a = \"xxx\";\n    private String output3b = \"yyy\";\n\n    @Field({ \"SOMETYPE:output1\", \"OTHERTYPE:output2\" })\n    public void setValue3(String name, String value) {\n        if (name.startsWith(\"SOMETYPE:\")) {\n            output3a = \"SOMETYPE3:\" + name + \":\" + value;\n        } else {\n            output3b = \"OTHERTYPE3:\" + name + \":\" + value;\n        }\n    }\n\n    public String getOutput3a() {\n        return output3a;\n    }\n\n    public String getOutput3b() {\n        return output3b;\n    }\n\n    private String output4a = \"X\";\n    private String output4b = \"Y\";\n\n    @Field({ \"SOMETYPE:output1\", \"OTHERTYPE:output2\", \"SOMETYPE:output1\", \"OTHERTYPE:output2\" })\n    public void setValue4(String name, String value) {\n        if (name.startsWith(\"SOMETYPE:\")) {\n            output4a = output4a + \"=SOMETYPE:\" + name + \":\" + value;\n        } else {\n            output4b = output4b + \"=OTHERTYPE:\" + name + \":\" + value;\n        }\n    }\n\n    public String getOutput4a() {\n        return output4a;\n    }\n\n    public String getOutput4b() {\n        return output4b;\n    }\n\n    private String output5a = \"X\";\n    private String output5b = \"Y\";\n\n    @Field({ \"SOMETYPE:output1\", \"OTHERTYPE:output2\", \"SOMETYPE:*\", \"OTHERTYPE:*\" })\n    public void setValue5(String name, String value) {\n        if (name.startsWith(\"SOMETYPE:\")) {\n            output5a = output5a + \"=SOMETYPE:\" + name + \":\" + value;\n        } else {\n            output5b = output5b + \"=OTHERTYPE:\" + name + \":\" + value;\n        }\n    }\n\n    public String getOutput5a() {\n        return output5a;\n    }\n\n    public String getOutput5b() {\n        return output5b;\n    }\n\n    private String output6 = \"Z\";\n\n    @Field({ \"FOO:output1.foo\" })\n    public void setValue6(String name, String value) {\n        output6 = output6 + \"=FOO:\" + name + \":\" + value;\n    }\n\n    public String getOutput6() {\n        return output6;\n    }\n\n    private String output7 = \"Z\";\n\n    @Field({ \"BAR:output1.bar\" })\n    public void setValue7(String name, String value) {\n        output7 = output7 + \"=BAR:\" + name + \":\" + value;\n    }\n\n    public String getOutput7() {\n        return output7;\n    }\n\n    private String output8 = \"Z\";\n\n    @Field({ \"WILD:output1.wild\" })\n    public void setValue8(String name, String value) {\n        output8 = output8 + \"=WILD:\" + name + \":\" + value;\n    }\n\n    public String getOutput8() {\n        return output8;\n    }\n\n}\n"
  },
  {
    "path": "parser-core/src/test/java/nl/basjes/parse/core/ParserResetTest.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.core;\n\nimport nl.basjes.parse.core.exceptions.DissectionFailure;\nimport nl.basjes.parse.core.exceptions.InvalidDissectorException;\nimport nl.basjes.parse.core.exceptions.MissingDissectorsException;\nimport nl.basjes.parse.core.test.NormalValuesDissector;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.ArrayList;\nimport java.util.EnumSet;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport static nl.basjes.parse.core.Casts.STRING_ONLY;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\nclass ParserResetTest {\n\n    public static class WildCardDissector extends SimpleDissector {\n\n        // Deliberate Dissector bug: We use a List instead of a Set so any duplicates are retained.\n        protected final List<String> outputNames = new ArrayList<>();\n\n        private static final Map<String, EnumSet<Casts>> OUTPUT_TYPES = new HashMap<>();\n        static {\n            OUTPUT_TYPES.put(\"EXTRA:*\", STRING_ONLY);\n        }\n        public WildCardDissector() {\n            super(\"STRING\", OUTPUT_TYPES);\n        }\n\n        @Override\n        public void dissect(Parsable<?> parsable, String inputname, Value value) throws DissectionFailure {\n            for (String outputName : outputNames) {\n                parsable.addDissection(inputname, \"EXTRA\", outputName, outputName);\n            }\n        }\n\n        @Override\n        public EnumSet<Casts> prepareForDissect(String inputname, String outputname) {\n            String name = outputname;\n            String prefix = inputname + '.';\n            if (outputname.startsWith(prefix)) {\n                name = outputname.substring(prefix.length());\n            }\n            outputNames.add(name);\n            return STRING_ONLY;\n        }\n    }\n\n    public static class DuplicateTestRecord {\n        private final Map<String, List<String>>  stringMap   = new HashMap<>(32);\n\n        public void setStringValue(final String name, final String value) {\n            stringMap.computeIfAbsent(name, s -> new ArrayList<>()).add(value);\n        }\n        public List<String> getStringValues(final String name) {\n            return stringMap.get(name);\n        }\n    }\n\n\n    @Test\n    void testParserReset() throws NoSuchMethodException, InvalidDissectorException, MissingDissectorsException, DissectionFailure {\n        Parser<DuplicateTestRecord> parser = new Parser<>(DuplicateTestRecord.class)\n            .setRootType(\"INPUT\")\n            .addDissector(new NormalValuesDissector())\n            .addDissector(new WildCardDissector())\n            .addParseTarget(\"setStringValue\", \"STRING:string\")\n            .addParseTarget(\"setStringValue\", \"EXTRA:string.one\")\n            .addParseTarget(\"setStringValue\", \"EXTRA:string.two\");\n\n        // This causes the parser to initialize\n        parser.getPossiblePaths();\n\n        // This should reset the parser completely\n        parser.addDissector(null);\n\n        DuplicateTestRecord testRecord = new DuplicateTestRecord();\n        parser.parse(testRecord, \"Doesn't matter\");\n\n        assertEquals(\"FortyTwo\", testRecord.getStringValues(\"STRING:string\").get(0));\n        assertEquals(\"one\",      testRecord.getStringValues(\"EXTRA:string.one\").get(0));\n        assertEquals(\"two\",      testRecord.getStringValues(\"EXTRA:string.two\").get(0));\n\n        // There used to be a bug that would make some values arrive twice.\n        assertEquals(1,          testRecord.getStringValues(\"STRING:string\").size());\n        assertEquals(1,          testRecord.getStringValues(\"EXTRA:string.one\").size());\n        assertEquals(1,          testRecord.getStringValues(\"EXTRA:string.two\").size());\n    }\n}\n"
  },
  {
    "path": "parser-core/src/test/java/nl/basjes/parse/core/ParserTypeColissionTest.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.core;\n\nimport nl.basjes.parse.core.exceptions.DissectionFailure;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.ArrayList;\nimport java.util.EnumSet;\nimport java.util.List;\n\nimport static nl.basjes.parse.core.Casts.STRING_ONLY;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\npublic class ParserTypeColissionTest {\n\n    public static class TestDissector extends Dissector {\n        private String inputType;\n        private String outputType;\n        private String outputName;\n        private String salt; // Each value that comes in is appended with this \"salt\"\n\n        public TestDissector(String inputType, String outputType, String outputName, String salt) {\n            this.inputType = inputType;\n            this.outputType = outputType;\n            this.outputName = outputName;\n            this.salt = salt;\n        }\n\n        public void init(String inputtype, String outputtype, String outputname, String saltt) {\n            this.inputType = inputtype;\n            this.outputType = outputtype;\n            this.outputName = outputname;\n            this.salt = saltt;\n        }\n\n        @Override\n        protected void initializeNewInstance(Dissector newInstance) {\n            ((TestDissector)newInstance).init(inputType, outputType, outputName, salt);\n        }\n\n        @Override\n        public void dissect(Parsable<?> parsable, String inputname)\n            throws DissectionFailure {\n            final ParsedField field = parsable.getParsableField(inputType, inputname);\n            parsable.addDissection(inputname, outputType, outputName, field.getValue().getString() + salt);\n        }\n\n        @Override\n        public String getInputType() {\n            return inputType;\n        }\n\n        @Override\n        public List<String> getPossibleOutput() {\n            List<String> result = new ArrayList<>();\n            result.add(outputType + \":\" + outputName);\n            return result;\n        }\n\n        @Override\n        public EnumSet<Casts> prepareForDissect(String inputname, String outputname) {\n            return STRING_ONLY;\n        }\n    }\n\n    public static class TestDissectorOne extends TestDissector {\n        public TestDissectorOne() {\n            super(\"INPUTTYPE\", \"SOMETYPE\", \"output\", \"+1\");\n        }\n    }\n\n    public static class TestDissectorTwo extends TestDissector {\n        public TestDissectorTwo() {\n            super(\"INPUTTYPE\", \"OTHERTYPE\", \"output\", \"+2\");\n        }\n    }\n\n    public static class TestDissectorSubOne extends TestDissector {\n        public TestDissectorSubOne() {\n            super(\"SOMETYPE\", \"SOMESUBTYPE\", \"output\", \"+S1\");\n        }\n    }\n\n    public static class TestDissectorSubTwo extends TestDissector {\n        public TestDissectorSubTwo() {\n            super(\"OTHERTYPE\", \"OTHERSUBTYPE\", \"output\", \"+S2\");\n        }\n    }\n\n    public static class TestDissectorSubSubOne extends TestDissector {\n        public TestDissectorSubSubOne() {\n            super(\"SOMESUBTYPE\", \"SOMESUBSUBTYPE\", \"output\", \"+SS1\");\n        }\n    }\n\n    public static class TestDissectorSubSubTwo extends TestDissector {\n        public TestDissectorSubSubTwo() {\n            super(\"OTHERSUBTYPE\", \"OTHERSUBSUBTYPE\", \"output\", \"+SS2\");\n        }\n    }\n\n    public static class TestParser<RECORD> extends Parser<RECORD> {\n        public TestParser(final Class<RECORD> clazz) {\n            super(clazz);\n            addDissector(new TestDissectorOne());\n            addDissector(new TestDissectorTwo());\n            addDissector(new TestDissectorSubOne());\n            addDissector(new TestDissectorSubTwo());\n            addDissector(new TestDissectorSubSubOne());\n            addDissector(new TestDissectorSubSubTwo());\n            setRootType(\"INPUTTYPE\");\n        }\n    }\n\n    public static class TestRecord {\n        private String output1 = \"xxx\";\n\n        @Field(\"SOMETYPE:output\")\n        public void setValue1(String name, String value) {\n            output1 = name + \":\" + value;\n        }\n\n        private String output2 = \"xxx\";\n\n        @Field(\"OTHERTYPE:output\")\n        public void setValue2(String name, String value) {\n            output2 = name + \":\" + value;\n        }\n\n        private String output3 = \"xxx\";\n\n        @Field(\"SOMESUBSUBTYPE:output.output.output\")\n        public void setValue3(String name, String value) {\n            output3 = name + \":\" + value;\n        }\n\n        private String output4 = \"xxx\";\n\n        @Field(\"OTHERSUBSUBTYPE:output.output.output\")\n        public void setValue4(String name, String value) {\n            output4 = name + \":\" + value;\n        }\n\n    }\n\n    @Test\n    void testParseString() throws Exception {\n        Parser<TestRecord> parser = new TestParser<>(TestRecord.class);\n\n        TestRecord output = new TestRecord();\n        parser.parse(output, \"Something\");\n        assertEquals(\"SOMETYPE:output:Something+1\", output.output1);\n        assertEquals(\"OTHERTYPE:output:Something+2\", output.output2);\n        assertEquals(\"SOMESUBSUBTYPE:output.output.output:Something+1+S1+SS1\", output.output3);\n        assertEquals(\"OTHERSUBSUBTYPE:output.output.output:Something+2+S2+SS2\", output.output4);\n    }\n\n}\n"
  },
  {
    "path": "parser-core/src/test/java/nl/basjes/parse/core/ParserTypeRemappingEdgeCase.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.core;\n\nimport nl.basjes.parse.core.exceptions.DissectionFailure;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.EnumSet;\nimport java.util.Map;\nimport java.util.TreeMap;\n\nimport static nl.basjes.parse.core.Casts.LONG_ONLY;\nimport static nl.basjes.parse.core.Casts.STRING_ONLY;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\npublic class ParserTypeRemappingEdgeCase {\n\n    public static class TestDissectorLongAsString extends SimpleDissector {\n\n        private static final Map<String, EnumSet<Casts>> OUTPUT = new TreeMap<>();\n        static {\n            OUTPUT.put(\"LONG_AS_STRING:long_as_string\", STRING_ONLY);\n        }\n\n        public TestDissectorLongAsString() {\n            super(\"INPUTTYPE\", OUTPUT);\n        }\n\n        @Override\n        public void dissect(Parsable<?> parsable, String inputname, Value value) throws DissectionFailure {\n            // This happens for example if you extract a query string parameter you know to be a number\n            parsable.addDissection(inputname, \"LONG_AS_STRING\", \"long_as_string\", \"42\");\n        }\n    }\n\n    public static class TestParser<RECORD> extends Parser<RECORD> {\n        public TestParser(final Class<RECORD> clazz) {\n            super(clazz);\n            addDissector(new TestDissectorLongAsString());\n            setRootType(\"INPUTTYPE\");\n        }\n    }\n\n    public static class Record {\n        String stringName1 = \"empty\";\n        String stringValue1 = \"empty\";\n        public void set1(String name, String value){\n            stringName1 = name;\n            stringValue1 = value;\n        }\n\n        String stringName2 = \"empty\";\n        String stringValue2 = \"empty\";\n        public void set2(String name, String value){\n            stringName2 = name;\n            stringValue2 = value;\n        }\n\n        String longName = \"empty\";\n        long longValue = 0;\n        public void set(String name, Long value){\n            longName = name;\n            longValue = value;\n        }\n    }\n\n    @Test\n    void testParseString() throws Exception {\n        Parser<Record> parser = new TestParser<>(Record.class);\n        parser\n            .addParseTarget(Record.class.getMethod(\"set1\", String.class, String.class), \"LONG_AS_STRING:long_as_string\")\n\n            // If we add a type remapping with a cast that does not include STRING it should still work.\n            .addTypeRemapping(\"long_as_string\", \"SOMETHING\", LONG_ONLY)\n            .addParseTarget(Record.class.getMethod(\"set\", String.class, Long.class), \"SOMETHING:long_as_string\")\n            // And this one should NOT be called\n            .addParseTarget(Record.class.getMethod(\"set2\", String.class, String.class), \"SOMETHING:long_as_string\");\n\n        Record output = new Record();\n        parser.parse(output, \"An input that does not matter\");\n        assertEquals(\"42\", output.stringValue1);\n        assertEquals(\"empty\", output.stringValue2);\n        assertEquals(42, output.longValue);\n    }\n\n}\n"
  },
  {
    "path": "parser-core/src/test/java/nl/basjes/parse/core/TestBadAPIUsage.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.core;\n\nimport nl.basjes.parse.core.exceptions.DissectionFailure;\nimport nl.basjes.parse.core.exceptions.InvalidDissectorException;\nimport nl.basjes.parse.core.exceptions.MissingDissectorsException;\nimport nl.basjes.parse.core.reference.BarDissector;\nimport nl.basjes.parse.core.reference.FooDissector;\nimport nl.basjes.parse.core.test.DissectorTester;\nimport nl.basjes.parse.core.test.NormalValuesDissector;\nimport nl.basjes.parse.core.test.TestRecord;\nimport org.junit.jupiter.api.Test;\n\nimport java.lang.reflect.Method;\nimport java.util.Collections;\nimport java.util.EnumSet;\nimport java.util.List;\n\nimport static nl.basjes.parse.core.Casts.STRING_ONLY;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\n\npublic class TestBadAPIUsage {\n\n    @Test\n    void testChangingInputTypeShouldNotBePossibleByDefault() {\n        assertThrows(InvalidDissectorException.class, () -> {\n            new DissectorTester.DummyDissector().setInputType(\"Change should not be allowed\");\n        });\n    }\n\n    @Test\n    void testDissectorString(){\n        assertEquals(\n            \"{ BarDissector : BARINPUT --> \" +\n                \"[ANY:barany, DOUBLE:bardouble, FLOAT:barfloat, INT:barint, LONG:barlong, STRING:barstring] }\",\n            new BarDissector().toString());\n    }\n\n    public static class NullInputDissector extends Dissector {\n        @Override\n        public void dissect(Parsable<?> parsable, String inputname) {\n        }\n        @Override\n        public String getInputType() {\n            return null;\n        }\n        @Override\n        public List<String> getPossibleOutput() {\n            return Collections.singletonList(\"FOO:foo\");\n        }\n        @Override\n        public EnumSet<Casts> prepareForDissect(String inputname, String outputname) {\n            return STRING_ONLY;\n        }\n    }\n\n    @Test\n    void testNullInputHandling() {\n        assertThrows(InvalidDissectorException.class, () -> {\n            new Parser<>(Object.class).addDissector(new NullInputDissector()).parse(\"Foo\");\n        });\n    }\n\n    public static class NullOutputDissector extends Dissector {\n        @Override\n        public void dissect(Parsable<?> parsable, String inputname) {\n        }\n        @Override\n        public String getInputType() {\n            return \"SOMETHING\";\n        }\n        @Override\n        public List<String> getPossibleOutput() {\n            return Collections.emptyList();\n        }\n        @Override\n        public EnumSet<Casts> prepareForDissect(String inputname, String outputname) {\n            return STRING_ONLY;\n        }\n    }\n\n    @Test\n    void testNullOutputHandling() {\n        assertThrows(InvalidDissectorException.class, () -> {\n            new Parser<>(Object.class).addDissector(new NullOutputDissector()).parse(\"Foo\");\n        });\n    }\n\n    public static class EmptyOutputDissector extends Dissector {\n        @Override\n        public void dissect(Parsable<?> parsable, String inputname) {\n        }\n        @Override\n        public String getInputType() {\n            return \"SOMETHING\";\n        }\n        @Override\n        public List<String> getPossibleOutput() {\n            return Collections.emptyList();\n        }\n        @Override\n        public EnumSet<Casts> prepareForDissect(String inputname, String outputname) {\n            return STRING_ONLY;\n        }\n    }\n\n    @Test\n    void testEmptyOutputHandling() {\n        assertThrows(InvalidDissectorException.class, () -> {\n            new Parser<>(Object.class).addDissector(new EmptyOutputDissector()).parse(\"Foo\");\n        });\n    }\n\n    @Test\n    void testFailZeroDissectors() {\n        assertThrows(MissingDissectorsException.class, () -> {\n            new Parser<>(TestRecord.class)\n                .setRootType(\"INPUT\")\n                .failOnMissingDissectors()\n                .addParseTarget(\"setStringValue\", \"SOMETHING:that.is.not.present\")\n                .addParseTarget(\"setStringValue\", \"STRING:string\")\n                .parse(\"Doesn't matter\");\n        });\n    }\n\n    @Test\n    void testFailOnMissingDissectors() {\n        assertThrows(MissingDissectorsException.class, () -> {\n            new Parser<>(TestRecord.class)\n                .setRootType(\"INPUT\")\n                .addDissector(new NormalValuesDissector())\n                .addDissector(new FooDissector())\n                .addDissector(new BarDissector())\n                .failOnMissingDissectors()\n                .addParseTarget(\"setStringValue\", \"SOMETHING:that.is.not.present\")\n                .addParseTarget(\"setStringValue\", \"STRING:string\")\n                .parse(\"Doesn't matter\");\n        });\n    }\n\n    @Test\n    void testIgnoreMissingDissectors() throws NoSuchMethodException, InvalidDissectorException, MissingDissectorsException, DissectionFailure {\n        new Parser<>(TestRecord.class)\n            .setRootType(\"INPUT\")\n            .addDissector(new NormalValuesDissector())\n            .addDissector(new FooDissector())\n            .addDissector(new BarDissector())\n            .ignoreMissingDissectors()\n            .addParseTarget(\"setStringValue\", Parser.SetterPolicy.ALWAYS, \"SOMETHING:that.is.not.present\")\n            .addParseTarget(\"setStringValue\", Parser.SetterPolicy.ALWAYS, \"STRING:string\")\n            .parse(\"Doesn't matter\");\n    }\n\n    @Test\n    void testNoSuchSetter() {\n        assertThrows(NoSuchMethodException.class, () -> {\n            new Parser<>(TestRecord.class)\n                .setRootType(\"INPUT\")\n                .addDissector(new NormalValuesDissector())\n                .addDissector(new FooDissector())\n                .addDissector(new BarDissector())\n                .ignoreMissingDissectors()\n                .addParseTarget(\"NoSetterWithThisName\", \"SOMETHING:that.is.not.present\")\n                .parse(\"Doesn't matter\");\n        });\n    }\n\n    @Test\n    void testBadParameters() throws NoSuchMethodException, InvalidDissectorException, MissingDissectorsException, DissectionFailure {\n        new Parser<>(TestRecord.class)\n            .setRootType(\"INPUT\")\n            .addDissector(new NormalValuesDissector())\n            .addDissector(new FooDissector())\n            .addDissector(new BarDissector())\n            .ignoreMissingDissectors()\n            .addParseTarget(\"setStringValue\", Parser.SetterPolicy.ALWAYS, \"SOMETHING:that.is.not.present\")\n            .addParseTarget(\"setStringValue\", Parser.SetterPolicy.ALWAYS, \"STRING:string\")\n            .addParseTarget(\"setStringValue\", null)\n            .addParseTarget((Method)null, \"foo\")\n            .parse(\"Doesn't matter\");\n    }\n\n    @Test\n    void testFieldCleanup() throws NoSuchMethodException, InvalidDissectorException, MissingDissectorsException, DissectionFailure {\n        new Parser<>(TestRecord.class)\n            .setRootType(\"INPUT\")\n            .addDissector(new NormalValuesDissector())\n            .addParseTarget(\"setStringValue\", \"stRinG:stRinG\")\n            .parse(\"Doesn't matter\")\n            .expectString(\"STRING:string\", \"FortyTwo\");\n    }\n\n\n}\n"
  },
  {
    "path": "parser-core/src/test/java/nl/basjes/parse/core/annotation/TestFieldSetters.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.core.annotation;\n\nimport nl.basjes.parse.core.Field;\nimport nl.basjes.parse.core.Parser;\nimport nl.basjes.parse.core.exceptions.DissectionFailure;\nimport nl.basjes.parse.core.exceptions.InvalidDissectorException;\nimport nl.basjes.parse.core.exceptions.MissingDissectorsException;\nimport nl.basjes.parse.core.test.EmptyValuesDissector;\nimport nl.basjes.parse.core.test.NormalValuesDissector;\nimport nl.basjes.parse.core.test.NullValuesDissector;\nimport nl.basjes.parse.core.test.TestRecord;\nimport org.junit.jupiter.api.Test;\n\nimport static nl.basjes.parse.core.Parser.SetterPolicy.ALWAYS;\nimport static nl.basjes.parse.core.Parser.SetterPolicy.NOT_EMPTY;\nimport static nl.basjes.parse.core.Parser.SetterPolicy.NOT_NULL;\n\n// CHECKSTYLE.OFF: ParenPad\n// CHECKSTYLE.OFF: LeftCurly\nclass TestFieldSetters {\n\n    public static class TestFieldSettersRecord extends TestRecord {\n        private void setS(String prefix, String name, String value) {\n            setStringValue(prefix + \"-\" + name, value);\n        }\n        private void setL(String prefix, String name, Long value) {\n            setLongValue(prefix + \"-\" + name, value);\n        }\n        private void setD(String prefix, String name, Double value) {\n            setDoubleValue(prefix + \"-\" + name, value);\n        }\n\n        @Field(value = \"ANY:any\"                                   ) public void setAD(String n, String v){ setS(\"D\", n, v); }\n        @Field(value = \"STRING:string\"                             ) public void setSD(String n, String v){ setS(\"D\", n, v); }\n        @Field(value = \"INT:int\"                                   ) public void setID(String n, String v){ setS(\"D\", n, v); }\n        @Field(value = \"LONG:long\"                                 ) public void setLD(String n, String v){ setS(\"D\", n, v); }\n        @Field(value = \"FLOAT:float\"                               ) public void setFD(String n, String v){ setS(\"D\", n, v); }\n        @Field(value = \"DOUBLE:double\"                             ) public void setDD(String n, String v){ setS(\"D\", n, v); }\n        @Field(value = \"ANY:any\"                                   ) public void setAD(String n, Long   v){ setL(\"D\", n, v); }\n        @Field(value = \"STRING:string\"                             ) public void setSD(String n, Long   v){ setL(\"D\", n, v); }\n        @Field(value = \"INT:int\"                                   ) public void setID(String n, Long   v){ setL(\"D\", n, v); }\n        @Field(value = \"LONG:long\"                                 ) public void setLD(String n, Long   v){ setL(\"D\", n, v); }\n        @Field(value = \"FLOAT:float\"                               ) public void setFD(String n, Long   v){ setL(\"D\", n, v); }\n        @Field(value = \"DOUBLE:double\"                             ) public void setDD(String n, Long   v){ setL(\"D\", n, v); }\n        @Field(value = \"ANY:any\"                                   ) public void setAD(String n, Double v){ setD(\"D\", n, v); }\n        @Field(value = \"STRING:string\"                             ) public void setSD(String n, Double v){ setD(\"D\", n, v); }\n        @Field(value = \"INT:int\"                                   ) public void setID(String n, Double v){ setD(\"D\", n, v); }\n        @Field(value = \"LONG:long\"                                 ) public void setLD(String n, Double v){ setD(\"D\", n, v); }\n        @Field(value = \"FLOAT:float\"                               ) public void setFD(String n, Double v){ setD(\"D\", n, v); }\n        @Field(value = \"DOUBLE:double\"                             ) public void setDD(String n, Double v){ setD(\"D\", n, v); }\n\n        @Field(value = \"ANY:any\",          setterPolicy = ALWAYS   ) public void setAA(String n, String v){ setS(\"A\", n, v); }\n        @Field(value = \"STRING:string\",    setterPolicy = ALWAYS   ) public void setSA(String n, String v){ setS(\"A\", n, v); }\n        @Field(value = \"INT:int\",          setterPolicy = ALWAYS   ) public void setIA(String n, String v){ setS(\"A\", n, v); }\n        @Field(value = \"LONG:long\",        setterPolicy = ALWAYS   ) public void setLA(String n, String v){ setS(\"A\", n, v); }\n        @Field(value = \"FLOAT:float\",      setterPolicy = ALWAYS   ) public void setFA(String n, String v){ setS(\"A\", n, v); }\n        @Field(value = \"DOUBLE:double\",    setterPolicy = ALWAYS   ) public void setDA(String n, String v){ setS(\"A\", n, v); }\n        @Field(value = \"ANY:any\",          setterPolicy = ALWAYS   ) public void setAA(String n, Long   v){ setL(\"A\", n, v); }\n        @Field(value = \"STRING:string\",    setterPolicy = ALWAYS   ) public void setSA(String n, Long   v){ setL(\"A\", n, v); }\n        @Field(value = \"INT:int\",          setterPolicy = ALWAYS   ) public void setIA(String n, Long   v){ setL(\"A\", n, v); }\n        @Field(value = \"LONG:long\",        setterPolicy = ALWAYS   ) public void setLA(String n, Long   v){ setL(\"A\", n, v); }\n        @Field(value = \"FLOAT:float\",      setterPolicy = ALWAYS   ) public void setFA(String n, Long   v){ setL(\"A\", n, v); }\n        @Field(value = \"DOUBLE:double\",    setterPolicy = ALWAYS   ) public void setDA(String n, Long   v){ setL(\"A\", n, v); }\n        @Field(value = \"ANY:any\",          setterPolicy = ALWAYS   ) public void setAA(String n, Double v){ setD(\"A\", n, v); }\n        @Field(value = \"STRING:string\",    setterPolicy = ALWAYS   ) public void setSA(String n, Double v){ setD(\"A\", n, v); }\n        @Field(value = \"INT:int\",          setterPolicy = ALWAYS   ) public void setIA(String n, Double v){ setD(\"A\", n, v); }\n        @Field(value = \"LONG:long\",        setterPolicy = ALWAYS   ) public void setLA(String n, Double v){ setD(\"A\", n, v); }\n        @Field(value = \"FLOAT:float\",      setterPolicy = ALWAYS   ) public void setFA(String n, Double v){ setD(\"A\", n, v); }\n        @Field(value = \"DOUBLE:double\",    setterPolicy = ALWAYS   ) public void setDA(String n, Double v){ setD(\"A\", n, v); }\n\n        @Field(value = \"ANY:any\",          setterPolicy = NOT_NULL ) public void setAN(String n, String v){ setS(\"N\", n, v); }\n        @Field(value = \"STRING:string\",    setterPolicy = NOT_NULL ) public void setSN(String n, String v){ setS(\"N\", n, v); }\n        @Field(value = \"INT:int\",          setterPolicy = NOT_NULL ) public void setIN(String n, String v){ setS(\"N\", n, v); }\n        @Field(value = \"LONG:long\",        setterPolicy = NOT_NULL ) public void setLN(String n, String v){ setS(\"N\", n, v); }\n        @Field(value = \"FLOAT:float\",      setterPolicy = NOT_NULL ) public void setFN(String n, String v){ setS(\"N\", n, v); }\n        @Field(value = \"DOUBLE:double\",    setterPolicy = NOT_NULL ) public void setDN(String n, String v){ setS(\"N\", n, v); }\n        @Field(value = \"ANY:any\",          setterPolicy = NOT_NULL ) public void setAN(String n, Long   v){ setL(\"N\", n, v); }\n        @Field(value = \"STRING:string\",    setterPolicy = NOT_NULL ) public void setSN(String n, Long   v){ setL(\"N\", n, v); }\n        @Field(value = \"INT:int\",          setterPolicy = NOT_NULL ) public void setIN(String n, Long   v){ setL(\"N\", n, v); }\n        @Field(value = \"LONG:long\",        setterPolicy = NOT_NULL ) public void setLN(String n, Long   v){ setL(\"N\", n, v); }\n        @Field(value = \"FLOAT:float\",      setterPolicy = NOT_NULL ) public void setFN(String n, Long   v){ setL(\"N\", n, v); }\n        @Field(value = \"DOUBLE:double\",    setterPolicy = NOT_NULL ) public void setDN(String n, Long   v){ setL(\"N\", n, v); }\n        @Field(value = \"ANY:any\",          setterPolicy = NOT_NULL ) public void setAN(String n, Double v){ setD(\"N\", n, v); }\n        @Field(value = \"STRING:string\",    setterPolicy = NOT_NULL ) public void setSN(String n, Double v){ setD(\"N\", n, v); }\n        @Field(value = \"INT:int\",          setterPolicy = NOT_NULL ) public void setIN(String n, Double v){ setD(\"N\", n, v); }\n        @Field(value = \"LONG:long\",        setterPolicy = NOT_NULL ) public void setLN(String n, Double v){ setD(\"N\", n, v); }\n        @Field(value = \"FLOAT:float\",      setterPolicy = NOT_NULL ) public void setFN(String n, Double v){ setD(\"N\", n, v); }\n        @Field(value = \"DOUBLE:double\",    setterPolicy = NOT_NULL ) public void setDN(String n, Double v){ setD(\"N\", n, v); }\n\n        @Field(value = \"ANY:any\",          setterPolicy = NOT_EMPTY) public void setAE(String n, String v){ setS(\"E\", n, v); }\n        @Field(value = \"STRING:string\",    setterPolicy = NOT_EMPTY) public void setSE(String n, String v){ setS(\"E\", n, v); }\n        @Field(value = \"INT:int\",          setterPolicy = NOT_EMPTY) public void setIE(String n, String v){ setS(\"E\", n, v); }\n        @Field(value = \"LONG:long\",        setterPolicy = NOT_EMPTY) public void setLE(String n, String v){ setS(\"E\", n, v); }\n        @Field(value = \"FLOAT:float\",      setterPolicy = NOT_EMPTY) public void setFE(String n, String v){ setS(\"E\", n, v); }\n        @Field(value = \"DOUBLE:double\",    setterPolicy = NOT_EMPTY) public void setDE(String n, String v){ setS(\"E\", n, v); }\n        @Field(value = \"ANY:any\",          setterPolicy = NOT_EMPTY) public void setAE(String n, Long   v){ setL(\"E\", n, v); }\n        @Field(value = \"STRING:string\",    setterPolicy = NOT_EMPTY) public void setSE(String n, Long   v){ setL(\"E\", n, v); }\n        @Field(value = \"INT:int\",          setterPolicy = NOT_EMPTY) public void setIE(String n, Long   v){ setL(\"E\", n, v); }\n        @Field(value = \"LONG:long\",        setterPolicy = NOT_EMPTY) public void setLE(String n, Long   v){ setL(\"E\", n, v); }\n        @Field(value = \"FLOAT:float\",      setterPolicy = NOT_EMPTY) public void setFE(String n, Long   v){ setL(\"E\", n, v); }\n        @Field(value = \"DOUBLE:double\",    setterPolicy = NOT_EMPTY) public void setDE(String n, Long   v){ setL(\"E\", n, v); }\n        @Field(value = \"ANY:any\",          setterPolicy = NOT_EMPTY) public void setAE(String n, Double v){ setD(\"E\", n, v); }\n        @Field(value = \"STRING:string\",    setterPolicy = NOT_EMPTY) public void setSE(String n, Double v){ setD(\"E\", n, v); }\n        @Field(value = \"INT:int\",          setterPolicy = NOT_EMPTY) public void setIE(String n, Double v){ setD(\"E\", n, v); }\n        @Field(value = \"LONG:long\",        setterPolicy = NOT_EMPTY) public void setLE(String n, Double v){ setD(\"E\", n, v); }\n        @Field(value = \"FLOAT:float\",      setterPolicy = NOT_EMPTY) public void setFE(String n, Double v){ setD(\"E\", n, v); }\n        @Field(value = \"DOUBLE:double\",    setterPolicy = NOT_EMPTY) public void setDE(String n, Double v){ setD(\"E\", n, v); }\n    }\n\n    @Test\n    void testNormalValues() throws InvalidDissectorException, MissingDissectorsException, DissectionFailure {\n        new Parser<>(TestFieldSettersRecord.class)\n            .setRootType(\"INPUT\")\n            .addDissector(new NormalValuesDissector())\n            .parse(\"Doesn't matter\")\n\n            // Default (== Always)\n            .expectString(  \"D-ANY:any\",        \"42\")\n            .expectString(  \"D-STRING:string\",  \"FortyTwo\")\n            .expectString(  \"D-INT:int\",        \"42\")\n            .expectString(  \"D-LONG:long\",      \"42\")\n            .expectString(  \"D-FLOAT:float\",    \"42.0\")\n            .expectString(  \"D-DOUBLE:double\",  \"42.0\")\n            .expectLong(    \"D-ANY:any\",        42L)\n            .noLong(     \"D-STRING:string\")\n            .expectLong(    \"D-INT:int\",        42L)\n            .expectLong(    \"D-LONG:long\",      42L)\n            .noLong(     \"D-FLOAT:float\")\n            .noLong(     \"D-DOUBLE:double\")\n            .expectDouble(  \"D-ANY:any\",        42D)\n            .noDouble(   \"D-STRING:string\")\n            .noDouble(   \"D-INT:int\")\n            .noDouble(   \"D-LONG:long\")\n            .expectDouble(  \"D-FLOAT:float\",    42D)\n            .expectDouble(  \"D-DOUBLE:double\",  42D)\n\n            // Always\n            .expectString(  \"A-ANY:any\",        \"42\")\n            .expectString(  \"A-STRING:string\",  \"FortyTwo\")\n            .expectString(  \"A-INT:int\",        \"42\")\n            .expectString(  \"A-LONG:long\",      \"42\")\n            .expectString(  \"A-FLOAT:float\",    \"42.0\")\n            .expectString(  \"A-DOUBLE:double\",  \"42.0\")\n            .expectLong(    \"A-ANY:any\",        42L)\n            .noLong(     \"A-STRING:string\")\n            .expectLong(    \"A-INT:int\",        42L)\n            .expectLong(    \"A-LONG:long\",      42L)\n            .noLong(     \"A-FLOAT:float\")\n            .noLong(     \"A-DOUBLE:double\")\n            .expectDouble(  \"A-ANY:any\",        42D)\n            .noDouble(   \"A-STRING:string\")\n            .noDouble(   \"A-INT:int\")\n            .noDouble(   \"A-LONG:long\")\n            .expectDouble(  \"A-FLOAT:float\",    42D)\n            .expectDouble(  \"A-DOUBLE:double\",  42D)\n\n            // Not Null\n            .expectString(  \"N-ANY:any\",        \"42\")\n            .expectString(  \"N-STRING:string\",  \"FortyTwo\")\n            .expectString(  \"N-INT:int\",        \"42\")\n            .expectString(  \"N-LONG:long\",      \"42\")\n            .expectString(  \"N-FLOAT:float\",    \"42.0\")\n            .expectString(  \"N-DOUBLE:double\",  \"42.0\")\n            .expectLong(    \"N-ANY:any\",        42L)\n            .noLong(     \"N-STRING:string\")\n            .expectLong(    \"N-INT:int\",        42L)\n            .expectLong(    \"N-LONG:long\",      42L)\n            .noLong(     \"N-FLOAT:float\")\n            .noLong(     \"N-DOUBLE:double\")\n            .expectDouble(  \"N-ANY:any\",        42D)\n            .noDouble(   \"N-STRING:string\")\n            .noDouble(   \"N-INT:int\")\n            .noDouble(   \"N-LONG:long\")\n            .expectDouble(  \"N-FLOAT:float\",    42D)\n            .expectDouble(  \"N-DOUBLE:double\",  42D)\n\n            // Not Empty\n            .expectString(  \"E-ANY:any\",        \"42\")\n            .expectString(  \"E-STRING:string\",  \"FortyTwo\")\n            .expectString(  \"E-INT:int\",        \"42\")\n            .expectString(  \"E-LONG:long\",      \"42\")\n            .expectString(  \"E-FLOAT:float\",    \"42.0\")\n            .expectString(  \"E-DOUBLE:double\",  \"42.0\")\n            .expectLong(    \"E-ANY:any\",        42L)\n            .noLong(     \"E-STRING:string\")\n            .expectLong(    \"E-INT:int\",        42L)\n            .expectLong(    \"E-LONG:long\",      42L)\n            .noLong(     \"E-FLOAT:float\")\n            .noLong(     \"E-DOUBLE:double\")\n            .expectDouble(  \"E-ANY:any\",        42D)\n            .noDouble(   \"E-STRING:string\")\n            .noDouble(   \"E-INT:int\")\n            .noDouble(   \"E-LONG:long\")\n            .expectDouble(  \"E-FLOAT:float\",    42D)\n            .expectDouble(  \"E-DOUBLE:double\",  42D);\n    }\n\n    @Test\n    void testEmptyValues() throws InvalidDissectorException, MissingDissectorsException, DissectionFailure {\n        Parser<TestFieldSettersRecord> parser = new Parser<>(TestFieldSettersRecord.class);\n        parser.setRootType(\"INPUT\");\n        parser.addDissector(new EmptyValuesDissector());\n        TestFieldSettersRecord testRecord = parser.parse(\"Doesn't matter\");\n\n        testRecord\n            // Default (== Always)\n            .expectString(  \"D-ANY:any\",        \"\")\n            .expectString(  \"D-STRING:string\",  \"\")\n            .expectString(  \"D-INT:int\",        \"\")\n            .expectString(  \"D-LONG:long\",      \"\")\n            .expectString(  \"D-FLOAT:float\",    \"\")\n            .expectString(  \"D-DOUBLE:double\",  \"\")\n            .expectLong(    \"D-ANY:any\",        (Long)null)\n            .noLong(     \"D-STRING:string\")\n            .expectLong(    \"D-INT:int\",        (Long)null)\n            .expectLong(    \"D-LONG:long\",      (Long)null)\n            .noLong(     \"D-FLOAT:float\")\n            .noLong(     \"D-DOUBLE:double\")\n            .expectDouble(  \"D-ANY:any\",        (Double)null)\n            .noDouble(   \"D-STRING:string\")\n            .noDouble(   \"D-INT:int\")\n            .noDouble(   \"D-LONG:long\")\n            .expectDouble(  \"D-FLOAT:float\",    (Double)null)\n            .expectDouble(  \"D-DOUBLE:double\",  (Double)null)\n\n            // Always\n            .expectString(  \"A-ANY:any\",        \"\")\n            .expectString(  \"A-STRING:string\",  \"\")\n            .expectString(  \"A-INT:int\",        \"\")\n            .expectString(  \"A-LONG:long\",      \"\")\n            .expectString(  \"A-FLOAT:float\",    \"\")\n            .expectString(  \"A-DOUBLE:double\",  \"\")\n            .expectLong(    \"A-ANY:any\",        (Long)null)\n            .noLong(     \"A-STRING:string\")\n            .expectLong(    \"A-INT:int\",        (Long)null)\n            .expectLong(    \"A-LONG:long\",      (Long)null)\n            .noLong(     \"A-FLOAT:float\")\n            .noLong(     \"A-DOUBLE:double\")\n            .expectDouble(  \"A-ANY:any\",        (Double)null)\n            .noDouble(   \"A-STRING:string\")\n            .noDouble(   \"A-INT:int\")\n            .noDouble(   \"A-LONG:long\")\n            .expectDouble(  \"A-FLOAT:float\",    (Double)null)\n            .expectDouble(  \"A-DOUBLE:double\",  (Double)null)\n\n            // Not Null\n            .expectString(  \"N-ANY:any\",        \"\")\n            .expectString(  \"N-STRING:string\",  \"\")\n            .expectString(  \"N-INT:int\",        \"\")\n            .expectString(  \"N-LONG:long\",      \"\")\n            .expectString(  \"N-FLOAT:float\",    \"\")\n            .expectString(  \"N-DOUBLE:double\",  \"\")\n            .noLong(     \"N-ANY:any\")\n            .noLong(     \"N-STRING:string\")\n            .noLong(     \"N-INT:int\")\n            .noLong(     \"N-LONG:long\")\n            .noLong(     \"N-FLOAT:float\")\n            .noLong(     \"N-DOUBLE:double\")\n            .noDouble(   \"N-ANY:any\")\n            .noDouble(   \"N-STRING:string\")\n            .noDouble(   \"N-INT:int\")\n            .noDouble(   \"N-LONG:long\")\n            .noDouble(   \"N-FLOAT:float\")\n            .noDouble(   \"N-DOUBLE:double\")\n\n            // Not Empty\n            .noString(  \"E-ANY:any\")\n            .noString(  \"E-STRING:string\")\n            .noString(  \"E-INT:int\")\n            .noString(  \"E-LONG:long\")\n            .noString(  \"E-FLOAT:float\")\n            .noString(  \"E-DOUBLE:double\")\n            .noLong(    \"E-ANY:any\")\n            .noLong(    \"E-STRING:string\")\n            .noLong(    \"E-INT:int\")\n            .noLong(    \"E-LONG:long\")\n            .noLong(    \"E-FLOAT:float\")\n            .noLong(    \"E-DOUBLE:double\")\n            .noDouble(  \"E-ANY:any\")\n            .noDouble(  \"E-STRING:string\")\n            .noDouble(  \"E-INT:int\")\n            .noDouble(  \"E-LONG:long\")\n            .noDouble(  \"E-FLOAT:float\")\n            .noDouble(  \"E-DOUBLE:double\");\n\n    }\n\n    @Test\n    void testNullValues() throws InvalidDissectorException, MissingDissectorsException, DissectionFailure {\n        Parser<TestFieldSettersRecord> parser = new Parser<>(TestFieldSettersRecord.class);\n        parser.setRootType(\"INPUT\");\n        parser.addDissector(new NullValuesDissector());\n        TestFieldSettersRecord testRecord = parser.parse(\"Doesn't matter\");\n\n        testRecord\n            // Default (== Always)\n            .expectString(  \"D-ANY:any\",        (String)null)\n            .expectString(  \"D-STRING:string\",  (String)null)\n            .expectString(  \"D-INT:int\",        (String)null)\n            .expectString(  \"D-LONG:long\",      (String)null)\n            .expectString(  \"D-FLOAT:float\",    (String)null)\n            .expectString(  \"D-DOUBLE:double\",  (String)null)\n            .expectLong(    \"D-ANY:any\",        (Long)null)\n            .noLong(     \"D-STRING:string\")\n            .expectLong(    \"D-INT:int\",        (Long)null)\n            .expectLong(    \"D-LONG:long\",      (Long)null)\n            .noLong(     \"D-FLOAT:float\")\n            .noLong(     \"D-DOUBLE:double\")\n            .expectDouble(  \"D-ANY:any\",        (Double)null)\n            .noDouble(   \"D-STRING:string\")\n            .noDouble(   \"D-INT:int\")\n            .noDouble(   \"D-LONG:long\")\n            .expectDouble(  \"D-FLOAT:float\",    (Double)null)\n            .expectDouble(  \"D-DOUBLE:double\",  (Double)null)\n\n            // Always\n            .expectString(  \"A-ANY:any\",        (String)null)\n            .expectString(  \"A-STRING:string\",  (String)null)\n            .expectString(  \"A-INT:int\",        (String)null)\n            .expectString(  \"A-LONG:long\",      (String)null)\n            .expectString(  \"A-FLOAT:float\",    (String)null)\n            .expectString(  \"A-DOUBLE:double\",  (String)null)\n            .expectLong(    \"A-ANY:any\",        (Long)null)\n            .noLong(     \"A-STRING:string\")\n            .expectLong(    \"A-INT:int\",        (Long)null)\n            .expectLong(    \"A-LONG:long\",      (Long)null)\n            .noLong(     \"A-FLOAT:float\")\n            .noLong(     \"A-DOUBLE:double\")\n            .expectDouble(  \"A-ANY:any\",        (Double)null)\n            .noDouble(   \"A-STRING:string\")\n            .noDouble(   \"A-INT:int\")\n            .noDouble(   \"A-LONG:long\")\n            .expectDouble(  \"A-FLOAT:float\",    (Double)null)\n            .expectDouble(  \"A-DOUBLE:double\",  (Double)null)\n\n            // Not Null\n            .noString(  \"N-ANY:any\")\n            .noString(  \"N-STRING:string\")\n            .noString(  \"N-INT:int\")\n            .noString(  \"N-LONG:long\")\n            .noString(  \"N-FLOAT:float\")\n            .noString(  \"N-DOUBLE:double\")\n            .noLong(    \"N-ANY:any\")\n            .noLong(    \"N-STRING:string\")\n            .noLong(    \"N-INT:int\")\n            .noLong(    \"N-LONG:long\")\n            .noLong(    \"N-FLOAT:float\")\n            .noLong(    \"N-DOUBLE:double\")\n            .noDouble(  \"N-ANY:any\")\n            .noDouble(  \"N-STRING:string\")\n            .noDouble(  \"N-INT:int\")\n            .noDouble(  \"N-LONG:long\")\n            .noDouble(  \"N-FLOAT:float\")\n            .noDouble(  \"N-DOUBLE:double\")\n\n            // Not Empty\n            .noString(  \"E-ANY:any\")\n            .noString(  \"E-STRING:string\")\n            .noString(  \"E-INT:int\")\n            .noString(  \"E-LONG:long\")\n            .noString(  \"E-FLOAT:float\")\n            .noString(  \"E-DOUBLE:double\")\n            .noLong(    \"E-ANY:any\")\n            .noLong(    \"E-STRING:string\")\n            .noLong(    \"E-INT:int\")\n            .noLong(    \"E-LONG:long\")\n            .noLong(    \"E-FLOAT:float\")\n            .noLong(    \"E-DOUBLE:double\")\n            .noDouble(  \"E-ANY:any\")\n            .noDouble(  \"E-STRING:string\")\n            .noDouble(  \"E-INT:int\")\n            .noDouble(  \"E-LONG:long\")\n            .noDouble(  \"E-FLOAT:float\")\n            .noDouble(  \"E-DOUBLE:double\");\n    }\n\n}\n"
  },
  {
    "path": "parser-core/src/test/java/nl/basjes/parse/core/annotation/TestFieldSettersAlwaysCombined.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.core.annotation;\n\nimport nl.basjes.parse.core.Field;\nimport nl.basjes.parse.core.Parser;\nimport nl.basjes.parse.core.exceptions.DissectionFailure;\nimport nl.basjes.parse.core.exceptions.InvalidDissectorException;\nimport nl.basjes.parse.core.exceptions.MissingDissectorsException;\nimport nl.basjes.parse.core.test.NormalValuesDissector;\nimport nl.basjes.parse.core.test.TestRecord;\nimport org.junit.jupiter.api.Test;\n\nimport static nl.basjes.parse.core.Parser.SetterPolicy.ALWAYS;\n\nclass TestFieldSettersAlwaysCombined {\n\n    public static class TestRecordString extends TestRecord {\n        @Field(value = {\n            \"ANY:any\",\n            \"STRING:string\",\n            \"INT:int\",\n            \"LONG:long\",\n            \"FLOAT:float\",\n            \"DOUBLE:double\" },\n            setterPolicy = ALWAYS)\n        public void set(String name, String value) {\n            setStringValue(name, value);\n        }\n    }\n\n    public static class TestRecordLong  extends TestRecord {\n        @Field(value = {\n            \"ANY:any\",\n            \"INT:int\",\n            \"LONG:long\" },\n            setterPolicy = ALWAYS)\n        public void set(String name, Long value) {\n            setLongValue(name, value);\n        }\n    }\n\n    public static class TestRecordDouble  extends TestRecord {\n        @Field(value = {\n            \"ANY:any\",\n            \"FLOAT:float\",\n            \"DOUBLE:double\" },\n            setterPolicy = ALWAYS)\n        public void set(String name, Double value) {\n            setDoubleValue(name, value);\n        }\n    }\n\n\n    @Test\n    void testString() throws InvalidDissectorException, MissingDissectorsException, DissectionFailure {\n        new Parser<>(TestRecordString.class)\n            .setRootType(\"INPUT\")\n            .addDissector(new NormalValuesDissector())\n            .parse(\"Doesn't matter\")\n\n            .expectString(\"ANY:any\",       \"42\")\n            .expectString(\"STRING:string\", \"FortyTwo\")\n            .expectString(\"INT:int\",       \"42\")\n            .expectString(\"LONG:long\",     \"42\")\n            .expectString(\"FLOAT:float\",   \"42.0\")\n            .expectString(\"DOUBLE:double\", \"42.0\");\n    }\n\n    @Test\n    void testLong() throws InvalidDissectorException, MissingDissectorsException, DissectionFailure {\n        new Parser<>(TestRecordLong.class)\n            .setRootType(\"INPUT\")\n            .addDissector(new NormalValuesDissector())\n            .parse(\"Doesn't matter\")\n\n            .expectLong(\"ANY:any\",    42L)\n            .expectLong(\"INT:int\",    42L)\n            .expectLong(\"LONG:long\",  42L);\n    }\n\n    @Test\n    void testDouble() throws InvalidDissectorException, MissingDissectorsException, DissectionFailure {\n        new Parser<>(TestRecordDouble.class)\n            .setRootType(\"INPUT\")\n            .addDissector(new NormalValuesDissector())\n            .parse(\"Doesn't matter\")\n\n            .expectDouble(\"ANY:any\",       42D)\n            .expectDouble(\"FLOAT:float\",   42D)\n            .expectDouble(\"DOUBLE:double\", 42D);\n    }\n\n}\n"
  },
  {
    "path": "parser-core/src/test/java/nl/basjes/parse/core/annotation/TestFieldSettersAlwaysSeparate.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.core.annotation;\n\nimport nl.basjes.parse.core.Field;\nimport nl.basjes.parse.core.Parser;\nimport nl.basjes.parse.core.exceptions.DissectionFailure;\nimport nl.basjes.parse.core.exceptions.InvalidDissectorException;\nimport nl.basjes.parse.core.exceptions.MissingDissectorsException;\nimport nl.basjes.parse.core.test.NormalValuesDissector;\nimport nl.basjes.parse.core.test.TestRecord;\nimport org.junit.jupiter.api.Test;\n\nimport static nl.basjes.parse.core.Parser.SetterPolicy.ALWAYS;\n\npublic class TestFieldSettersAlwaysSeparate {\n\n    public static class TestRecordString extends TestRecord {\n        private void set(String name, String value) {\n            setStringValue(name, value);\n        }\n\n        // CHECKSTYLE.OFF: LeftCurly\n        @Field(value = \"ANY:any\",       setterPolicy = ALWAYS) public void setA(String n, String v) { set(n, v); }\n        @Field(value = \"STRING:string\", setterPolicy = ALWAYS) public void setS(String n, String v) { set(n, v); }\n        @Field(value = \"INT:int\",       setterPolicy = ALWAYS) public void setI(String n, String v) { set(n, v); }\n        @Field(value = \"LONG:long\",     setterPolicy = ALWAYS) public void setL(String n, String v) { set(n, v); }\n        @Field(value = \"FLOAT:float\",   setterPolicy = ALWAYS) public void setF(String n, String v) { set(n, v); }\n        @Field(value = \"DOUBLE:double\", setterPolicy = ALWAYS) public void setD(String n, String v) { set(n, v); }\n        // CHECKSTYLE.ON: LeftCurly\n    }\n\n    public static class TestRecordLong extends TestRecord {\n        private void set(String name, Long value) {\n            setLongValue(name, value);\n        }\n\n        // CHECKSTYLE.OFF: LeftCurly\n        @Field(value = \"ANY:any\",       setterPolicy = ALWAYS) public void setA(String n, Long v) { set(n, v); }\n        @Field(value = \"INT:int\",       setterPolicy = ALWAYS) public void setI(String n, Long v) { set(n, v); }\n        @Field(value = \"LONG:long\",     setterPolicy = ALWAYS) public void setL(String n, Long v) { set(n, v); }\n        // CHECKSTYLE.ON: LeftCurly\n    }\n\n    public static class TestRecordDouble extends TestRecord {\n        private void set(String name, Double value) {\n            setDoubleValue(name, value);\n        }\n\n        // CHECKSTYLE.OFF: LeftCurly\n        @Field(value = \"ANY:any\",       setterPolicy = ALWAYS) public void setA(String n, Double v) { set(n, v); }\n        @Field(value = \"FLOAT:float\",   setterPolicy = ALWAYS) public void setF(String n, Double v) { set(n, v); }\n        @Field(value = \"DOUBLE:double\", setterPolicy = ALWAYS) public void setD(String n, Double v) { set(n, v); }\n        // CHECKSTYLE.ON: LeftCurly\n    }\n\n    @Test\n    void testString() throws InvalidDissectorException, MissingDissectorsException, DissectionFailure {\n        new Parser<>(TestRecordString.class)\n            .setRootType(\"INPUT\")\n            .addDissector(new NormalValuesDissector())\n            .parse(\"Doesn't matter\")\n\n            .expectString(\"ANY:any\",       \"42\")\n            .expectString(\"STRING:string\", \"FortyTwo\")\n            .expectString(\"INT:int\",       \"42\")\n            .expectString(\"LONG:long\",     \"42\")\n            .expectString(\"FLOAT:float\",   \"42.0\")\n            .expectString(\"DOUBLE:double\", \"42.0\");\n    }\n\n    @Test\n    void testLong() throws InvalidDissectorException, MissingDissectorsException, DissectionFailure {\n        new Parser<>(TestRecordLong.class)\n            .setRootType(\"INPUT\")\n            .addDissector(new NormalValuesDissector())\n            .parse(\"Doesn't matter\")\n\n            .expectLong(\"ANY:any\",    42L)\n            .expectLong(\"INT:int\",    42L)\n            .expectLong(\"LONG:long\",  42L);\n    }\n\n    @Test\n    void testDouble() throws InvalidDissectorException, MissingDissectorsException, DissectionFailure {\n        new Parser<>(TestRecordDouble.class)\n            .setRootType(\"INPUT\")\n            .addDissector(new NormalValuesDissector())\n            .parse(\"Doesn't matter\")\n\n            .expectDouble(\"ANY:any\",       42D)\n            .expectDouble(\"FLOAT:float\",   42D)\n            .expectDouble(\"DOUBLE:double\", 42D);\n    }\n\n}\n"
  },
  {
    "path": "parser-core/src/test/java/nl/basjes/parse/core/annotation/TestFieldSettersNotEmpty.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.core.annotation;\n\nimport nl.basjes.parse.core.Field;\nimport nl.basjes.parse.core.Parser;\nimport nl.basjes.parse.core.exceptions.DissectionFailure;\nimport nl.basjes.parse.core.exceptions.InvalidDissectorException;\nimport nl.basjes.parse.core.exceptions.MissingDissectorsException;\nimport nl.basjes.parse.core.test.EmptyValuesDissector;\nimport nl.basjes.parse.core.test.TestRecord;\nimport org.junit.jupiter.api.Test;\n\nimport static nl.basjes.parse.core.Parser.SetterPolicy.NOT_EMPTY;\n\npublic class TestFieldSettersNotEmpty {\n\n    public static class TestRecordString extends TestRecord {\n        @Field(value = {\n            \"ANY:any\",\n            \"STRING:string\",\n            \"INT:int\",\n            \"LONG:long\",\n            \"FLOAT:float\",\n            \"DOUBLE:double\" },\n            setterPolicy = NOT_EMPTY)\n        public void set(String name, String value) {\n            setStringValue(name, value);\n        }\n\n        // CHECKSTYLE.OFF: LeftCurly\n        @Field(value = \"ANY:any\",       setterPolicy = NOT_EMPTY) public void setA(String n, String v) { set(n, v); }\n        @Field(value = \"STRING:string\", setterPolicy = NOT_EMPTY) public void setS(String n, String v) { set(n, v); }\n        @Field(value = \"INT:int\",       setterPolicy = NOT_EMPTY) public void setI(String n, String v) { set(n, v); }\n        @Field(value = \"LONG:long\",     setterPolicy = NOT_EMPTY) public void setL(String n, String v) { set(n, v); }\n        @Field(value = \"FLOAT:float\",   setterPolicy = NOT_EMPTY) public void setF(String n, String v) { set(n, v); }\n        @Field(value = \"DOUBLE:double\", setterPolicy = NOT_EMPTY) public void setD(String n, String v) { set(n, v); }\n        // CHECKSTYLE.ON: LeftCurly\n    }\n\n    public static class TestRecordLong extends TestRecord {\n        @Field(value = {\n            \"ANY:any\",\n            \"INT:int\",\n            \"LONG:long\" },\n            setterPolicy = NOT_EMPTY)\n        public void set(String name, Long value) {\n            setLongValue(name, value);\n        }\n\n        // CHECKSTYLE.OFF: LeftCurly\n        @Field(value = \"ANY:any\",       setterPolicy = NOT_EMPTY) public void setA(String n, Long v) { set(n, v); }\n        @Field(value = \"INT:int\",       setterPolicy = NOT_EMPTY) public void setI(String n, Long v) { set(n, v); }\n        @Field(value = \"LONG:long\",     setterPolicy = NOT_EMPTY) public void setL(String n, Long v) { set(n, v); }\n        // CHECKSTYLE.ON: LeftCurly\n    }\n\n    public static class TestRecordDouble extends TestRecord {\n        @Field(value = {\n            \"ANY:any\",\n            \"FLOAT:float\",\n            \"DOUBLE:double\" },\n            setterPolicy = NOT_EMPTY)\n        public void set(String name, Double value) {\n            setDoubleValue(name, value);\n        }\n\n        // CHECKSTYLE.OFF: LeftCurly\n        @Field(value = \"ANY:any\",       setterPolicy = NOT_EMPTY) public void setA(String n, Double v) { set(n, v); }\n        @Field(value = \"FLOAT:float\",   setterPolicy = NOT_EMPTY) public void setF(String n, Double v) { set(n, v); }\n        @Field(value = \"DOUBLE:double\", setterPolicy = NOT_EMPTY) public void setD(String n, Double v) { set(n, v); }\n        // CHECKSTYLE.ON: LeftCurly\n    }\n\n    @Test\n    void testString() throws InvalidDissectorException, MissingDissectorsException, DissectionFailure {\n        new Parser<>(TestRecordString.class)\n            .setRootType(\"INPUT\")\n            .addDissector(new EmptyValuesDissector())\n            .parse(\"Doesn't matter\")\n\n            .noString(\"ANY:any\")\n            .noString(\"STRING:string\")\n            .noString(\"INT:int\")\n            .noString(\"LONG:long\")\n            .noString(\"FLOAT:float\")\n            .noString(\"DOUBLE:double\");\n    }\n\n    @Test\n    void testLong() throws InvalidDissectorException, MissingDissectorsException, DissectionFailure {\n        new Parser<>(TestRecordLong.class)\n            .setRootType(\"INPUT\")\n            .addDissector(new EmptyValuesDissector())\n            .parse(\"Doesn't matter\")\n\n            .noLong(\"ANY:any\")\n            .noLong(\"INT:int\")\n            .noLong(\"LONG:long\");\n    }\n\n    @Test\n    void testDouble() throws InvalidDissectorException, MissingDissectorsException, DissectionFailure {\n        new Parser<>(TestRecordDouble.class)\n            .setRootType(\"INPUT\")\n            .addDissector(new EmptyValuesDissector())\n            .parse(\"Doesn't matter\")\n\n            .noDouble(\"ANY:any\")\n            .noDouble(\"FLOAT:float\")\n            .noDouble(\"DOUBLE:double\");\n    }\n\n}\n"
  },
  {
    "path": "parser-core/src/test/java/nl/basjes/parse/core/annotation/TestFieldSettersNotNull.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.core.annotation;\n\nimport nl.basjes.parse.core.Field;\nimport nl.basjes.parse.core.Parser;\nimport nl.basjes.parse.core.exceptions.DissectionFailure;\nimport nl.basjes.parse.core.exceptions.InvalidDissectorException;\nimport nl.basjes.parse.core.exceptions.MissingDissectorsException;\nimport nl.basjes.parse.core.test.NullValuesDissector;\nimport nl.basjes.parse.core.test.TestRecord;\nimport org.junit.jupiter.api.Test;\n\nimport static nl.basjes.parse.core.Parser.SetterPolicy.NOT_NULL;\n\npublic class TestFieldSettersNotNull {\n\n    public static class TestRecordString extends TestRecord {\n        @Field(value = {\n            \"ANY:any\",\n            \"STRING:string\",\n            \"INT:int\",\n            \"LONG:long\",\n            \"FLOAT:float\",\n            \"DOUBLE:double\" },\n            setterPolicy = NOT_NULL)\n        public void set(String name, String value) {\n            setStringValue(name, value);\n        }\n\n        // CHECKSTYLE.OFF: LeftCurly\n        @Field(value = \"ANY:any\",       setterPolicy = NOT_NULL) public void setA(String n, String v) { set(n, v); }\n        @Field(value = \"STRING:string\", setterPolicy = NOT_NULL) public void setS(String n, String v) { set(n, v); }\n        @Field(value = \"INT:int\",       setterPolicy = NOT_NULL) public void setI(String n, String v) { set(n, v); }\n        @Field(value = \"LONG:long\",     setterPolicy = NOT_NULL) public void setL(String n, String v) { set(n, v); }\n        @Field(value = \"FLOAT:float\",   setterPolicy = NOT_NULL) public void setF(String n, String v) { set(n, v); }\n        @Field(value = \"DOUBLE:double\", setterPolicy = NOT_NULL) public void setD(String n, String v) { set(n, v); }\n        // CHECKSTYLE.ON: LeftCurly\n    }\n\n    public static class TestRecordLong extends TestRecord {\n        @Field(value = {\n            \"ANY:any\",\n            \"INT:int\",\n            \"LONG:long\" },\n            setterPolicy = NOT_NULL)\n        public void set(String name, Long value) {\n            setLongValue(name, value);\n        }\n\n        // CHECKSTYLE.OFF: LeftCurly\n        @Field(value = \"ANY:any\",       setterPolicy = NOT_NULL) public void setA(String n, Long v) { set(n, v); }\n        @Field(value = \"INT:int\",       setterPolicy = NOT_NULL) public void setI(String n, Long v) { set(n, v); }\n        @Field(value = \"LONG:long\",     setterPolicy = NOT_NULL) public void setL(String n, Long v) { set(n, v); }\n        // CHECKSTYLE.ON: LeftCurly\n    }\n\n    public static class TestRecordDouble extends TestRecord {\n        @Field(value = {\n            \"ANY:any\",\n            \"FLOAT:float\",\n            \"DOUBLE:double\" },\n            setterPolicy = NOT_NULL)\n        public void set(String name, Double value) {\n            setDoubleValue(name, value);\n        }\n\n        // CHECKSTYLE.OFF: LeftCurly\n        @Field(value = \"ANY:any\",       setterPolicy = NOT_NULL) public void setA(String n, Double v) { set(n, v); }\n        @Field(value = \"FLOAT:float\",   setterPolicy = NOT_NULL) public void setF(String n, Double v) { set(n, v); }\n        @Field(value = \"DOUBLE:double\", setterPolicy = NOT_NULL) public void setD(String n, Double v) { set(n, v); }\n        // CHECKSTYLE.ON: LeftCurly\n    }\n\n    @Test\n    void testString() throws InvalidDissectorException, MissingDissectorsException, DissectionFailure {\n        new Parser<>(TestRecordString.class)\n            .setRootType(\"INPUT\")\n            .addDissector(new NullValuesDissector())\n            .parse(\"Doesn't matter\")\n\n            .noString(\"ANY:any\")\n            .noString(\"STRING:string\")\n            .noString(\"INT:int\")\n            .noString(\"LONG:long\")\n            .noString(\"FLOAT:float\")\n            .noString(\"DOUBLE:double\");\n    }\n\n    @Test\n    void testLong() throws InvalidDissectorException, MissingDissectorsException, DissectionFailure {\n        new Parser<>(TestRecordLong.class)\n            .setRootType(\"INPUT\")\n            .addDissector(new NullValuesDissector())\n            .parse(\"Doesn't matter\")\n\n            .noLong(\"ANY:any\")\n            .noLong(\"INT:int\")\n            .noLong(\"LONG:long\");\n    }\n\n    @Test\n    void testDouble() throws InvalidDissectorException, MissingDissectorsException, DissectionFailure {\n        new Parser<>(TestRecordDouble.class)\n            .setRootType(\"INPUT\")\n            .addDissector(new NullValuesDissector())\n            .parse(\"Doesn't matter\")\n\n            .noDouble(\"ANY:any\")\n            .noDouble(\"FLOAT:float\")\n            .noDouble(\"DOUBLE:double\");\n    }\n\n}\n"
  },
  {
    "path": "parser-core/src/test/java/nl/basjes/parse/core/convert/ValueConvertTest.java",
    "content": "/*\n * Apache HTTPD logparsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.core.convert;\n\nimport nl.basjes.parse.core.Casts;\nimport nl.basjes.parse.core.Dissector;\nimport nl.basjes.parse.core.Parsable;\nimport nl.basjes.parse.core.SimpleDissector;\nimport nl.basjes.parse.core.Value;\nimport nl.basjes.parse.core.exceptions.DissectionFailure;\nimport nl.basjes.parse.core.exceptions.InvalidDissectorException;\nimport nl.basjes.parse.core.test.DissectorTester;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.EnumSet;\nimport java.util.HashMap;\nimport java.util.List;\n\nimport static nl.basjes.parse.core.Casts.STRING_OR_LONG;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\npublic class ValueConvertTest {\n\n    @Test\n    public void verifyTypeConversionStoM() {\n        DissectorTester.create()\n            .withDissector(\"something\", new SecondsToMilliseconds())\n            .withDissector(new MillisecondsToSeconds())\n            .withInput(\"12345\") // Which is in seconds because that dissector is 'first'\n            .expect(\"SECONDS:something\", \"12345\")\n            .expect(\"MILLISECONDS:something\", \"12345000\")\n//            .verbose()\n            .checkExpectations();\n    }\n\n    @Test\n    public void verifyTypeConversionMtoS() {\n        DissectorTester.create()\n            .withDissector(\"something\", new MillisecondsToSeconds())\n            .withDissector(new SecondsToMilliseconds())\n            .withInput(\"12345000\") // Which is in MILLIseconds because that dissector is 'first'\n            .expect(\"SECONDS:something\", \"12345\")\n            .expect(\"MILLISECONDS:something\", \"12345000\")\n//            .verbose()\n            .checkExpectations();\n    }\n\n    @Test\n    public void verifyTypeConversionPossibleFields() {\n        List<String> possible = DissectorTester.create()\n            .withDissector(\"something\", new MillisecondsToSeconds())\n            .withDissector(new SecondsToMilliseconds())\n            .withInput(\"12345000\") // Which is in MILLIseconds because that dissector is 'first'\n            .getPossible();\n\n        assertTrue(possible.contains(\"MILLISECONDS:something\"));\n        assertTrue(possible.contains(\"SECONDS:something\"));\n    }\n\n    public abstract static class TypeConvertBaseDissector extends SimpleDissector {\n        protected String inputType;\n        protected String outputType;\n\n        private static HashMap<String, EnumSet<Casts>> fillOutputConfig(String outputType, EnumSet<Casts> casts) {\n            HashMap<String, EnumSet<Casts>> typeConvertConfig = new HashMap<>();\n            typeConvertConfig.put(outputType + \":\", casts);\n            return typeConvertConfig;\n        }\n\n        public TypeConvertBaseDissector(String nInputType, String nOutputType) {\n            super(nInputType, fillOutputConfig(nOutputType, STRING_OR_LONG));\n            inputType = nInputType;\n            outputType = nOutputType;\n        }\n\n        @Override\n        protected void initializeNewInstance(Dissector newInstance) throws InvalidDissectorException {\n            super.initializeNewInstance(newInstance);\n            ((TypeConvertBaseDissector) newInstance).inputType = inputType;\n            ((TypeConvertBaseDissector) newInstance).outputType = outputType;\n        }\n    }\n\n    public static class SecondsToMilliseconds extends TypeConvertBaseDissector {\n\n        public SecondsToMilliseconds() {\n            super(\"SECONDS\", \"MILLISECONDS\");\n        }\n\n        @Override\n        public void dissect(Parsable<?> parsable, String inputname, Value value) throws DissectionFailure {\n            parsable.addDissection(inputname, \"MILLISECONDS\", \"\", value.getLong() * 1000);\n        }\n    }\n\n    public static class MillisecondsToSeconds extends TypeConvertBaseDissector {\n        public MillisecondsToSeconds() {\n            super(\"MILLISECONDS\", \"SECONDS\");\n        }\n\n        @Override\n        public void dissect(Parsable<?> parsable, String inputname, Value value) throws DissectionFailure {\n            parsable.addDissection(inputname, \"SECONDS\", \"\", value.getLong() / 1000);\n        }\n    }\n\n}\n"
  },
  {
    "path": "parser-core/src/test/java/nl/basjes/parse/core/reference/BarDissector.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.core.reference;\n\nimport nl.basjes.parse.core.Casts;\nimport nl.basjes.parse.core.Parsable;\nimport nl.basjes.parse.core.SimpleDissector;\nimport nl.basjes.parse.core.Value;\nimport nl.basjes.parse.core.exceptions.DissectionFailure;\n\nimport java.util.EnumSet;\nimport java.util.HashMap;\n\nimport static nl.basjes.parse.core.Casts.STRING_ONLY;\nimport static nl.basjes.parse.core.Casts.STRING_OR_DOUBLE;\nimport static nl.basjes.parse.core.Casts.STRING_OR_LONG;\nimport static nl.basjes.parse.core.Casts.STRING_OR_LONG_OR_DOUBLE;\n\npublic class BarDissector extends SimpleDissector {\n\n    private static final HashMap<String, EnumSet<Casts>> DISSECTOR_CONFIG = new HashMap<>();\n    static {\n        DISSECTOR_CONFIG.put(\"ANY:barany\",         STRING_OR_LONG_OR_DOUBLE);\n        DISSECTOR_CONFIG.put(\"STRING:barstring\",   STRING_ONLY);\n        DISSECTOR_CONFIG.put(\"INT:barint\",         STRING_OR_LONG);\n        DISSECTOR_CONFIG.put(\"LONG:barlong\",       STRING_OR_LONG);\n        DISSECTOR_CONFIG.put(\"FLOAT:barfloat\",     STRING_OR_DOUBLE);\n        DISSECTOR_CONFIG.put(\"DOUBLE:bardouble\",   STRING_OR_DOUBLE);\n    }\n\n    public BarDissector() {\n        super(\"BARINPUT\", DISSECTOR_CONFIG);\n    }\n\n    @Override\n    public void dissect(Parsable<?> parsable, String inputname, Value value) throws DissectionFailure {\n        parsable.addDissection(inputname, \"ANY\",    \"barany\",    \"42\");\n        parsable.addDissection(inputname, \"STRING\", \"barstring\", \"42\");\n        parsable.addDissection(inputname, \"INT\",    \"barint\",    42);\n        parsable.addDissection(inputname, \"LONG\",   \"barlong\",   42L);\n        parsable.addDissection(inputname, \"FLOAT\",  \"barfloat\",  42F);\n        parsable.addDissection(inputname, \"DOUBLE\", \"bardouble\", 42D);\n    }\n}\n"
  },
  {
    "path": "parser-core/src/test/java/nl/basjes/parse/core/reference/FooDissector.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.core.reference;\n\nimport nl.basjes.parse.core.Casts;\nimport nl.basjes.parse.core.Parsable;\nimport nl.basjes.parse.core.SimpleDissector;\nimport nl.basjes.parse.core.Value;\nimport nl.basjes.parse.core.exceptions.DissectionFailure;\n\nimport java.util.EnumSet;\nimport java.util.HashMap;\n\nimport static nl.basjes.parse.core.Casts.STRING_ONLY;\nimport static nl.basjes.parse.core.Casts.STRING_OR_DOUBLE;\nimport static nl.basjes.parse.core.Casts.STRING_OR_LONG;\nimport static nl.basjes.parse.core.Casts.STRING_OR_LONG_OR_DOUBLE;\n\npublic class FooDissector extends SimpleDissector {\n\n    private static final HashMap<String, EnumSet<Casts>> DISSECTOR_CONFIG = new HashMap<>();\n    static {\n        DISSECTOR_CONFIG.put(\"ANY:fooany\",         STRING_OR_LONG_OR_DOUBLE);\n        DISSECTOR_CONFIG.put(\"STRING:foostring\",   STRING_ONLY);\n        DISSECTOR_CONFIG.put(\"INT:fooint\",         STRING_OR_LONG);\n        DISSECTOR_CONFIG.put(\"LONG:foolong\",       STRING_OR_LONG);\n        DISSECTOR_CONFIG.put(\"FLOAT:foofloat\",     STRING_OR_DOUBLE);\n        DISSECTOR_CONFIG.put(\"DOUBLE:foodouble\",   STRING_OR_DOUBLE);\n    }\n\n    public FooDissector() {\n        super(\"FOOINPUT\", DISSECTOR_CONFIG);\n    }\n\n    @Override\n    public void dissect(Parsable<?> parsable, String inputname, Value value) throws DissectionFailure {\n        parsable.addDissection(inputname, \"ANY\",    \"fooany\",    \"42\");\n        parsable.addDissection(inputname, \"STRING\", \"foostring\", \"42\");\n        parsable.addDissection(inputname, \"INT\",    \"fooint\",    42);\n        parsable.addDissection(inputname, \"LONG\",   \"foolong\",   42L);\n        parsable.addDissection(inputname, \"FLOAT\",  \"foofloat\",  42F);\n        parsable.addDissection(inputname, \"DOUBLE\", \"foodouble\", 42D);\n    }\n}\n"
  },
  {
    "path": "parser-core/src/test/java/nl/basjes/parse/core/reference/FooSpecialDissector.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.core.reference;\n\nimport nl.basjes.parse.core.Parser;\n\npublic class FooSpecialDissector extends FooDissector {\n    @Override\n    public <RECORD> void createAdditionalDissectors(Parser<RECORD> parser) {\n        parser.addDissector(new BarDissector());\n        parser.addTypeRemapping(\"foostring\", \"BARINPUT\");\n    }\n}\n"
  },
  {
    "path": "parser-core/src/test/java/nl/basjes/parse/core/reference/ReferenceTest.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.core.reference;\n\nimport nl.basjes.parse.core.Parser;\nimport nl.basjes.parse.core.test.DissectorTester;\nimport nl.basjes.parse.core.test.TestRecord;\nimport org.junit.jupiter.api.Test;\n\npublic class ReferenceTest {\n\n    @Test\n    public void verifyFoo() {\n        DissectorTester.create()\n            .withDissector(new FooDissector())\n            .withInput(\"Doesn't matter\")\n            .expect(\"ANY:fooany\",        \"42\")\n            .expect(\"ANY:fooany\",        42L)\n            .expect(\"ANY:fooany\",        42D)\n            .expect(\"STRING:foostring\",  \"42\")\n            .expectAbsentLong(\"STRING:foostring\")\n            .expectAbsentDouble(\"STRING:foostring\")\n            .expect(\"INT:fooint\",        \"42\")\n            .expect(\"INT:fooint\",        42L)\n            .expectAbsentDouble(\"INT:fooint\")\n            .expect(\"LONG:foolong\",      \"42\")\n            .expect(\"LONG:foolong\",      42L)\n            .expectAbsentDouble(\"LONG:foolong\")\n            .expect(\"FLOAT:foofloat\",    \"42.0\")\n            .expectAbsentLong(\"FLOAT:foofloat\")\n            .expect(\"FLOAT:foofloat\",    42D)\n            .expect(\"DOUBLE:foodouble\",  \"42.0\")\n            .expectAbsentLong(\"DOUBLE:foodouble\")\n            .expect(\"DOUBLE:foodouble\",  42D)\n//            .verbose()\n            .checkExpectations();\n    }\n\n    @Test\n    public void verifyBar() {\n        DissectorTester.create()\n            .withDissector(new BarDissector())\n            .withInput(\"Doesn't matter\")\n            .expect(\"ANY:barany\",        \"42\")\n            .expect(\"ANY:barany\",        42L)\n            .expect(\"ANY:barany\",        42D)\n            .expect(\"STRING:barstring\",  \"42\")\n            .expectAbsentLong(\"STRING:barstring\")\n            .expectAbsentDouble(\"STRING:barstring\")\n            .expect(\"INT:barint\",        \"42\")\n            .expect(\"INT:barint\",        42L)\n            .expectAbsentDouble(\"INT:barint\")\n            .expect(\"LONG:barlong\",      \"42\")\n            .expect(\"LONG:barlong\",      42L)\n            .expectAbsentDouble(\"LONG:barlong\")\n            .expect(\"FLOAT:barfloat\",    \"42.0\")\n            .expectAbsentLong(\"FLOAT:barfloat\")\n            .expect(\"FLOAT:barfloat\",    42D)\n            .expect(\"DOUBLE:bardouble\",  \"42.0\")\n            .expectAbsentLong(\"DOUBLE:bardouble\")\n            .expect(\"DOUBLE:bardouble\",  42D)\n//            .verbose()\n            .checkExpectations();\n    }\n\n    @Test\n    public void runManuallyCombined(){\n        Parser<TestRecord> parser = new Parser<>(TestRecord.class);\n        parser.addDissector(new FooDissector());\n        parser.addDissector(new BarDissector());\n        parser.addTypeRemapping(\"foostring\", \"BARINPUT\");\n        parser.setRootType(new FooDissector().getInputType());\n\n        DissectorTester.create()\n            .withParser(parser)\n            .withInput(\"BlaBlaBla\")\n\n            .expect(\"ANY:fooany\",                   \"42\")\n            .expect(\"ANY:fooany\",                   42L)\n            .expect(\"ANY:fooany\",                   42D)\n            .expect(\"STRING:foostring\",             \"42\")\n            .expectAbsentLong(\"STRING:foostring\")\n            .expectAbsentDouble(\"STRING:foostring\")\n            .expect(\"INT:fooint\",                   \"42\")\n            .expect(\"INT:fooint\",                   42L)\n            .expectAbsentDouble(\"INT:fooint\")\n            .expect(\"LONG:foolong\",                 \"42\")\n            .expect(\"LONG:foolong\",                 42L)\n            .expectAbsentDouble(\"LONG:foolong\")\n            .expect(\"FLOAT:foofloat\",               \"42.0\")\n            .expectAbsentLong(\"FLOAT:foofloat\")\n            .expect(\"FLOAT:foofloat\",               42D)\n            .expect(\"DOUBLE:foodouble\",             \"42.0\")\n            .expectAbsentLong(\"DOUBLE:foodouble\")\n            .expect(\"DOUBLE:foodouble\",             42D)\n\n            .expect(\"ANY:foostring.barany\",         \"42\")\n            .expect(\"ANY:foostring.barany\",         42L)\n            .expect(\"ANY:foostring.barany\",         42D)\n            .expect(\"STRING:foostring.barstring\",   \"42\")\n            .expectAbsentLong(\"STRING:foostring.barstring\")\n            .expectAbsentDouble(\"STRING:foostring.barstring\")\n            .expect(\"INT:foostring.barint\",         \"42\")\n            .expect(\"INT:foostring.barint\",         42L)\n            .expectAbsentDouble(\"INT:foostring.barint\")\n            .expect(\"LONG:foostring.barlong\",       \"42\")\n            .expect(\"LONG:foostring.barlong\",       42L)\n            .expectAbsentDouble(\"LONG:foostring.barlong\")\n            .expect(\"FLOAT:foostring.barfloat\",     \"42.0\")\n            .expectAbsentLong(\"FLOAT:foostring.barfloat\")\n            .expect(\"FLOAT:foostring.barfloat\",     42D)\n            .expect(\"DOUBLE:foostring.bardouble\",   \"42.0\")\n            .expectAbsentLong(\"DOUBLE:foostring.bardouble\")\n            .expect(\"DOUBLE:foostring.bardouble\",   42D)\n\n            .checkExpectations();\n    }\n\n    @Test\n    public void runAutomaticallyAddedBar(){\n        DissectorTester.create()\n            .withDissector(new FooSpecialDissector())\n            .withInput(\"BlaBlaBla\")\n\n            .expect(\"ANY:fooany\",                   \"42\")\n            .expect(\"ANY:fooany\",                   42L)\n            .expect(\"ANY:fooany\",                   42D)\n            .expect(\"STRING:foostring\",             \"42\")\n            .expectAbsentLong(\"STRING:foostring\")\n            .expectAbsentDouble(\"STRING:foostring\")\n            .expect(\"INT:fooint\",                 \"42\")\n            .expect(\"INT:fooint\",                 42L)\n            .expectAbsentDouble(\"INT:fooint\")\n            .expect(\"LONG:foolong\",                 \"42\")\n            .expect(\"LONG:foolong\",                 42L)\n            .expectAbsentDouble(\"LONG:foolong\")\n            .expect(\"FLOAT:foofloat\",             \"42.0\")\n            .expectAbsentLong(\"FLOAT:foofloat\")\n            .expect(\"FLOAT:foofloat\",             42D)\n            .expect(\"DOUBLE:foodouble\",             \"42.0\")\n            .expectAbsentLong(\"DOUBLE:foodouble\")\n            .expect(\"DOUBLE:foodouble\",             42D)\n\n            .expect(\"ANY:foostring.barany\",         \"42\")\n            .expect(\"ANY:foostring.barany\",         42L)\n            .expect(\"ANY:foostring.barany\",         42D)\n            .expect(\"STRING:foostring.barstring\",   \"42\")\n            .expectAbsentLong(\"STRING:foostring.barstring\")\n            .expectAbsentDouble(\"STRING:foostring.barstring\")\n            .expect(\"INT:foostring.barint\",       \"42\")\n            .expect(\"INT:foostring.barint\",       42L)\n            .expectAbsentDouble(\"INT:foostring.barint\")\n            .expect(\"LONG:foostring.barlong\",       \"42\")\n            .expect(\"LONG:foostring.barlong\",       42L)\n            .expectAbsentDouble(\"LONG:foostring.barlong\")\n            .expect(\"FLOAT:foostring.barfloat\",   \"42.0\")\n            .expectAbsentLong(\"FLOAT:foostring.barfloat\")\n            .expect(\"FLOAT:foostring.barfloat\",   42D)\n            .expect(\"DOUBLE:foostring.bardouble\",   \"42.0\")\n            .expectAbsentLong(\"DOUBLE:foostring.bardouble\")\n            .expect(\"DOUBLE:foostring.bardouble\",   42D)\n\n            .checkExpectations();\n    }\n\n}\n"
  },
  {
    "path": "parser-core/src/test/java/nl/basjes/parse/core/reference/ReferenceTestDouble.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.core.reference;\n\nimport nl.basjes.parse.core.Casts;\nimport nl.basjes.parse.core.Dissector;\nimport nl.basjes.parse.core.Parsable;\nimport nl.basjes.parse.core.exceptions.DissectionFailure;\nimport nl.basjes.parse.core.test.DissectorTester;\nimport org.junit.jupiter.api.Test;\n\nimport java.util.Collections;\nimport java.util.EnumSet;\nimport java.util.List;\n\nimport static nl.basjes.parse.core.Casts.STRING_ONLY;\n\n/**\n * These tests validate what happens if two dissectors want the SAME input to work on\n */\npublic class ReferenceTestDouble {\n\n    @Test\n    public void verifyRemap() {\n        DissectorTester.create()\n            .withDissector(new RemapInputDissector())\n            .withInput(\"Doesn't matter\")\n            .expect(\"INPUT:\",  \"42\")\n            .checkExpectations();\n    }\n\n\n    @Test\n    public void verifyFooInput() {\n        DissectorTester.create()\n            .withDissector(new FooInputDissector())\n            .withInput(\"Doesn't matter\")\n            .expect(\"ANY:fooany\",                   \"42\")\n            .expect(\"ANY:fooany\",                   42L)\n            .expect(\"ANY:fooany\",                   42D)\n            .expect(\"STRING:foostring\",             \"42\")\n            .expectAbsentLong(\"STRING:foostring\")\n            .expectAbsentDouble(\"STRING:foostring\")\n            .expect(\"INT:fooint\",                   \"42\")\n            .expect(\"INT:fooint\",                   42L)\n            .expectAbsentDouble(\"INT:fooint\")\n            .expect(\"LONG:foolong\",                 \"42\")\n            .expect(\"LONG:foolong\",                 42L)\n            .expectAbsentDouble(\"LONG:foolong\")\n            .expect(\"FLOAT:foofloat\",               \"42.0\")\n            .expectAbsentLong(\"FLOAT:foofloat\")\n            .expect(\"FLOAT:foofloat\",               42D)\n            .expect(\"DOUBLE:foodouble\",             \"42.0\")\n            .expectAbsentLong(\"DOUBLE:foodouble\")\n            .expect(\"DOUBLE:foodouble\",             42D)\n//            .verbose()\n            .checkExpectations();\n    }\n\n    @Test\n    public void verifyBarInput() {\n        DissectorTester.create()\n            .withDissector(new BarInputDissector())\n            .withInput(\"Doesn't matter\")\n            .expect(\"ANY:barany\",        \"42\")\n            .expect(\"ANY:barany\",        42L)\n            .expect(\"ANY:barany\",        42D)\n            .expect(\"STRING:barstring\",  \"42\")\n            .expectAbsentLong(\"STRING:barstring\")\n            .expectAbsentDouble(\"STRING:barstring\")\n            .expect(\"INT:barint\",        \"42\")\n            .expect(\"INT:barint\",        42L)\n            .expectAbsentDouble(\"INT:barint\")\n            .expect(\"LONG:barlong\",      \"42\")\n            .expect(\"LONG:barlong\",      42L)\n            .expectAbsentDouble(\"LONG:barlong\")\n            .expect(\"FLOAT:barfloat\",    \"42.0\")\n            .expectAbsentLong(\"FLOAT:barfloat\")\n            .expect(\"FLOAT:barfloat\",    42D)\n            .expect(\"DOUBLE:bardouble\",  \"42.0\")\n            .expectAbsentLong(\"DOUBLE:bardouble\")\n            .expect(\"DOUBLE:bardouble\",  42D)\n//            .verbose()\n            .checkExpectations();\n    }\n\n    @Test\n    public void runDoubleDissectors(){\n        DissectorTester.create()\n//            .verbose()\n\n            .withDissector(new InputCreatingDissector())\n            .withDissector(new RemapInputDissector())\n            .withDissector(new FooInputDissector())\n            .withDissector(new BarInputDissector())\n            .withInput(\"Doesn't matter\")\n\n            .expect(\"ANY:something.fooany\",           \"42\")\n            .expect(\"ANY:something.fooany\",           42L)\n            .expect(\"ANY:something.fooany\",           42D)\n            .expect(\"STRING:something.foostring\",     \"42\")\n            .expectAbsentLong(\"STRING:something.foostring\")\n            .expectAbsentDouble(\"STRING:something.foostring\")\n            .expect(\"INT:something.fooint\",           \"42\")\n            .expect(\"INT:something.fooint\",           42L)\n            .expectAbsentDouble(\"INT:something.fooint\")\n            .expect(\"LONG:something.foolong\",         \"42\")\n            .expect(\"LONG:something.foolong\",         42L)\n            .expectAbsentDouble(\"LONG:something.foolong\")\n            .expect(\"FLOAT:something.foofloat\",       \"42.0\")\n            .expectAbsentLong(\"FLOAT:something.foofloat\")\n            .expect(\"FLOAT:something.foofloat\",       42D)\n            .expect(\"DOUBLE:something.foodouble\",     \"42.0\")\n            .expectAbsentLong(\"DOUBLE:something.foodouble\")\n            .expect(\"DOUBLE:something.foodouble\",     42D)\n\n            .expect(\"ANY:something.barany\",           \"42\")\n            .expect(\"ANY:something.barany\",           42L)\n            .expect(\"ANY:something.barany\",           42D)\n            .expect(\"STRING:something.barstring\",     \"42\")\n            .expectAbsentLong(\"STRING:something.barstring\")\n            .expectAbsentDouble(\"STRING:something.barstring\")\n            .expect(\"INT:something.barint\",           \"42\")\n            .expect(\"INT:something.barint\",           42L)\n            .expectAbsentDouble(\"INT:something.barint\")\n            .expect(\"LONG:something.barlong\",         \"42\")\n            .expect(\"LONG:something.barlong\",         42L)\n            .expectAbsentDouble(\"LONG:something.barlong\")\n            .expect(\"FLOAT:something.barfloat\",       \"42.0\")\n            .expectAbsentLong(\"FLOAT:something.barfloat\")\n            .expect(\"FLOAT:something.barfloat\",       42D)\n            .expect(\"DOUBLE:something.bardouble\",     \"42.0\")\n            .expectAbsentLong(\"DOUBLE:something.bardouble\")\n            .expect(\"DOUBLE:something.bardouble\",     42D)\n\n//            .printPossible()\n//            .printDissectors()\n            .checkExpectations();\n    }\n\n\n    public static class InputCreatingDissector extends Dissector {\n        @Override\n        public void dissect(Parsable<?> parsable, String inputname) throws DissectionFailure {\n            parsable.addDissection(inputname, \"BASEINPUT\", \"something\", \"42\");\n        }\n\n        @Override\n        public String getInputType() {\n            return \"SOME_LINE\";\n        }\n\n        @Override\n        public List<String> getPossibleOutput() {\n            return Collections.singletonList(\"BASEINPUT:something\");\n        }\n\n        @Override\n        public EnumSet<Casts> prepareForDissect(String inputname, String outputname) {\n            return STRING_ONLY;\n        }\n    }\n\n\n    public static class RemapInputDissector extends Dissector {\n        @Override\n        public void dissect(Parsable<?> parsable, String inputname) throws DissectionFailure {\n            parsable.addDissection(inputname, \"INPUT\", \"\", \"42\");\n        }\n\n        @Override\n        public String getInputType() {\n            return \"BASEINPUT\";\n        }\n\n        @Override\n        public List<String> getPossibleOutput() {\n            return Collections.singletonList(\"INPUT:\");\n        }\n\n        @Override\n        public EnumSet<Casts> prepareForDissect(String inputname, String outputname) {\n            return STRING_ONLY;\n        }\n    }\n\n\n    public static class FooInputDissector extends FooDissector {\n        @Override\n        public String getInputType() {\n            return \"INPUT\";\n        }\n    }\n\n    public static class BarInputDissector extends BarDissector {\n        @Override\n        public String getInputType() {\n            return \"INPUT\";\n        }\n    }\n\n}\n"
  },
  {
    "path": "parser-core/src/test/java/nl/basjes/parse/core/test/DissectorTester.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.core.test;\n\nimport nl.basjes.parse.core.Casts;\nimport nl.basjes.parse.core.Dissector;\nimport nl.basjes.parse.core.Parsable;\nimport nl.basjes.parse.core.ParsedField;\nimport nl.basjes.parse.core.Parser;\nimport nl.basjes.parse.core.exceptions.DissectionFailure;\nimport nl.basjes.parse.core.exceptions.InvalidDissectorException;\nimport nl.basjes.parse.core.exceptions.MissingDissectorsException;\nimport org.apache.commons.lang3.SerializationUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.EnumSet;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.TreeMap;\nimport java.util.regex.Pattern;\nimport java.util.stream.Collectors;\n\nimport static nl.basjes.parse.core.Casts.STRING_ONLY;\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\nimport static org.junit.jupiter.api.Assertions.fail;\n\npublic final class DissectorTester implements Serializable {\n\n    private static final Logger LOG = LoggerFactory.getLogger(DissectorTester.class);\n\n    private boolean verbose = false;\n    private final List<String> inputValues = new ArrayList<>();\n    private final Map<String, String> expectedStrings = new TreeMap<>();\n    private final Map<String, Long> expectedLongs = new TreeMap<>();\n    private final Map<String, Double> expectedDoubles = new TreeMap<>();\n    private final List<String> expectedValuePresent = new ArrayList<>();\n    private final List<String> expectedAbsentStrings = new ArrayList<>();\n    private final List<String> expectedAbsentLongs = new ArrayList<>();\n    private final List<String> expectedAbsentDoubles = new ArrayList<>();\n    private final List<String> expectedPossible = new ArrayList<>();\n    private Parser<TestRecord> parser = new Parser<>(TestRecord.class);\n    private String pathPrefix = \"\";\n\n    private DissectorTester() {\n    }\n\n    public static DissectorTester create() {\n        return new DissectorTester();\n    }\n\n    public DissectorTester withParser(Parser<TestRecord> newParser) {\n        this.parser = newParser;\n        return this;\n    }\n\n    /**\n     * Wildcard dissectors at the root doesn't work (yet)\n     * So to test these we can create a dummy root dissector that simply inserts a \"first level dissector that does nothing\"\n     * @param fieldName The fieldName of the first level path\n     * @param dissector The first REAL dissector for this test\n     * @return This DissectorTester\n     */\n    public DissectorTester withDissector(String fieldName, Dissector dissector) {\n        return withDissector(new DummyDissector(dissector.getInputType(), fieldName))\n                .withDissector(dissector);\n    }\n\n    public DissectorTester withDissector(Dissector dissector) {\n        parser.addDissector(dissector);\n        if (parser.getAllDissectors().size() == 1) {\n            parser.setRootType(dissector.getInputType());\n        }\n        return this;\n    }\n\n    public DissectorTester withInput(String inputValue) {\n        this.inputValues.add(inputValue);\n        return this;\n    }\n\n    private void addStringSetter(String fieldname) {\n        try {\n            parser.addParseTarget(TestRecord.class.getMethod(\"setStringValue\", String.class, String.class), fieldname);\n        } catch (NoSuchMethodException e) {\n            e.printStackTrace();\n            fail(e.getMessage());\n        }\n    }\n\n    private void addLongSetter(String fieldname) {\n        try {\n            parser.addParseTarget(TestRecord.class.getMethod(\"setLongValue\", String.class, Long.class), fieldname);\n        } catch (NoSuchMethodException e) {\n            e.printStackTrace();\n            fail(e.getMessage());\n        }\n    }\n\n    private void addDoubleSetter(String fieldname) {\n        try {\n            parser.addParseTarget(TestRecord.class.getMethod(\"setDoubleValue\", String.class, Double.class), fieldname);\n        } catch (NoSuchMethodException e) {\n            e.printStackTrace();\n            fail(e.getMessage());\n        }\n    }\n\n    public DissectorTester expect(String fieldname, String expected) {\n        fieldname = addPrefix(fieldname);\n        expectedStrings.put(fieldname, expected);\n        addStringSetter(fieldname);\n        return this;\n    }\n\n    public DissectorTester expect(String fieldname, Long expected) {\n        fieldname = addPrefix(fieldname);\n        expectedLongs.put(fieldname, expected);\n        addLongSetter(fieldname);\n        return this;\n    }\n\n    public DissectorTester expect(String fieldname, Integer expected) {\n        return expect(fieldname, Long.valueOf(expected));\n    }\n\n    public DissectorTester expect(String fieldname, Double expected) {\n        fieldname = addPrefix(fieldname);\n        expectedDoubles.put(fieldname, expected);\n        addDoubleSetter(fieldname);\n        return this;\n    }\n\n    public DissectorTester expect(String fieldname, Float expected) {\n        return expect(fieldname, Double.valueOf(expected));\n    }\n\n    public DissectorTester expectNull(String fieldname) {\n        fieldname = addPrefix(fieldname);\n        expectedStrings.put(fieldname, null);\n        addStringSetter(fieldname);\n        return this;\n    }\n\n    public DissectorTester expectValuePresent(String fieldname) {\n        fieldname = addPrefix(fieldname);\n        expectedValuePresent.add(fieldname);\n        try {\n            parser.addParseTarget(TestRecord.class.getMethod(\"setStringValue\", String.class, String.class), fieldname);\n        } catch (NoSuchMethodException e) {\n            e.printStackTrace();\n        }\n        return this;\n    }\n\n    public DissectorTester expectAbsentString(String fieldname) {\n        fieldname = addPrefix(fieldname);\n        expectedAbsentStrings.add(fieldname);\n        addStringSetter(fieldname);\n        return this;\n    }\n\n    public DissectorTester expectAbsentLong(String fieldname) {\n        fieldname = addPrefix(fieldname);\n        expectedAbsentLongs.add(fieldname);\n        addLongSetter(fieldname);\n        return this;\n    }\n\n    public DissectorTester expectAbsentDouble(String fieldname) {\n        fieldname = addPrefix(fieldname);\n        expectedAbsentDoubles.add(fieldname);\n        addDoubleSetter(fieldname);\n        return this;\n    }\n\n    public DissectorTester expectPossible(String fieldname) {\n        fieldname = addPrefix(fieldname);\n        expectedPossible.add(fieldname);\n        return this;\n    }\n\n    public DissectorTester verbose() {\n        this.verbose = true;\n        return this;\n    }\n\n    private static final Pattern PREFIX_INSERTER = Pattern.compile(\"([^:]+:)([^:]+)\");\n\n    public DissectorTester withPathPrefix(String prefix) {\n        if (prefix == null || prefix.isEmpty()) {\n            pathPrefix = \"\";\n        } else {\n            pathPrefix = \"$1\" + prefix + \"$2\";\n        }\n        return this;\n    }\n\n    String addPrefix(String field) {\n        if (pathPrefix.isEmpty()) {\n            return field;\n        }\n        return PREFIX_INSERTER.matcher(field).replaceAll(pathPrefix);\n    }\n\n    private static class ExpectationResult {\n        final String expectation;\n        final String field;\n        final String value;\n        final String failReason;\n\n        ExpectationResult(String expectation, String field, Object value, String failReason) {\n            this.expectation = expectation;\n            this.field = field;\n            if (value == null) {\n                this.value = null;\n            } else {\n                this.value = value.toString();\n            }\n            this.failReason = failReason;\n        }\n    }\n\n    private void expectEquals(List<ExpectationResult> expectationResults, String fieldName, String msg, Object left, Object right) {\n        boolean expression = false;\n        String expected = \"<<<null>>>\";\n        if (left == null) {\n            if (right == null) {\n                expression = true;\n            }\n        } else {\n            expected = left.toString();\n            expression = left.equals(right);\n        }\n        if (expression) {\n            expectationResults.add(new ExpectationResult(msg, fieldName, expected, null));\n        } else {\n            if (right == null) {\n                expectationResults.add(new ExpectationResult(msg, fieldName, expected, \"Wrong value: <<<null>>>\"));\n            } else {\n                expectationResults.add(new ExpectationResult(msg, fieldName, expected, \"Wrong value: \"+right));\n            }\n        }\n    }\n\n    public DissectorTester checkExpectations() {\n        DissectorTester tester = SerializationUtils.clone(this);\n        try {\n            return tester.checkExpectationsDirect();\n        } catch (AssertionError ae) {\n            throw new AssertionError(ae.getMessage());\n        }\n    }\n\n    private DissectorTester checkExpectationsDirect() {\n        if (expectedStrings.isEmpty() &&\n            expectedLongs.isEmpty() &&\n            expectedDoubles.isEmpty() &&\n\n            expectedValuePresent.isEmpty() &&\n\n            expectedAbsentStrings.isEmpty() &&\n            expectedAbsentLongs.isEmpty() &&\n            expectedAbsentDoubles.isEmpty() &&\n\n            expectedPossible.isEmpty()) {\n            fail(\"No expected values were specified\");\n        }\n        List<ExpectationResult> results = new ArrayList<>(32);\n\n        results.addAll(checkDissectors());\n        results.addAll(checkExpectedValues());\n        results.addAll(checkExpectedAbsent());\n        results.addAll(checkExpectedPossible());\n\n        summarizeResults(results);\n        return this;\n    }\n\n    private void summarizeResults(List<ExpectationResult> results) {\n        boolean success = true;\n        final String headerField            = \"Field\";\n        final String headerCheck            = \"Check\";\n        final String headerExpectedValue    = \"Expected Value\";\n        final String headerFailReason       = \"Fail reason\";\n        int maxExpectation                  = headerField        .length();\n        int maxFieldName                    = headerCheck        .length();\n        int maxExpectedValue                = headerExpectedValue.length();\n        int maxFailReason                   = headerFailReason   .length();\n\n        for (ExpectationResult expectationResult: results) {\n            maxExpectation = Math.max(maxExpectation, expectationResult.expectation.length());\n            maxFieldName = Math.max(maxFieldName, expectationResult.field.length());\n            if (expectationResult.value != null) {\n                maxExpectedValue = Math.max(maxExpectedValue, expectationResult.value.length());\n            }\n            if (expectationResult.failReason != null) {\n                success = false;\n                maxFailReason = Math.max(maxFailReason, expectationResult.failReason.length());\n            }\n        }\n        if (!success) {\n            StringBuilder sb = new StringBuilder(1024);\n            sb.append(\"\\n[     ] /\").append(padding(\"\", maxExpectation+maxFieldName+maxExpectedValue+maxFailReason+11, '=')).append(\"\\\\\\n\");\n            sb\n                .append(\"[     ] | \")\n                .append(headerField).append(padding(headerField, maxFieldName))\n                .append(\" | \")\n                .append(headerCheck).append(padding(headerCheck, maxExpectation))\n                .append(\" | \")\n                .append(headerExpectedValue).append(padding(headerExpectedValue, maxExpectedValue))\n                .append(\" | \")\n                .append(headerFailReason).append(padding(headerFailReason, maxFailReason))\n                .append(\" |\")\n                .append(\"\\n[     ] +\")\n                    .append(padding(\"\", maxFieldName+2, '-')).append('+')\n                    .append(padding(\"\", maxExpectation+2, '-')).append('+')\n                    .append(padding(\"\", maxExpectedValue+2, '-')).append('+')\n                    .append(padding(\"\", maxFailReason+2, '-')).append('+')\n                .append(\"\\n\");\n\n            for (ExpectationResult expectationResult: results) {\n                if (expectationResult.failReason == null) {\n                    sb.append(\"[     ] \");\n                } else {\n                    sb.append(\"[ERROR] \");\n                }\n                sb\n                    .append(\"| \")\n                    .append(expectationResult.field).append(padding(expectationResult.field, maxFieldName))\n                    .append(\" | \")\n                    .append(expectationResult.expectation).append(padding(expectationResult.expectation, maxExpectation))\n                    .append(\" | \");\n                String value = expectationResult.value;\n                if (expectationResult.value == null) {\n                    value = \" \";\n                }\n                sb\n                    .append(padding(value, maxExpectedValue)).append(value).append(\" | \");\n                if (expectationResult.failReason == null) {\n                    sb.append(padding(\"\", maxFailReason));\n                } else {\n                    sb.append(expectationResult.failReason).append(padding(expectationResult.failReason, maxFailReason));\n                }\n                sb.append(\" |\\n\");\n            }\n            sb.append(\"[     ] \\\\\").append(padding(\"\", maxExpectation+maxFieldName+maxExpectedValue+maxFailReason+11, '=')).append(\"/\\n\");\n            fail(sb.toString());\n        }\n    }\n\n    private List<ExpectationResult> checkExpectedValues() {\n        if (expectedStrings.size() +\n            expectedLongs.size() +\n            expectedDoubles.size() +\n            expectedValuePresent.size() == 0) {\n            return Collections.emptyList(); // Nothing to do here\n        }\n\n        if (inputValues.isEmpty()) {\n            fail(\"No inputvalues were specified\");\n        }\n\n        List<ExpectationResult> expectationResults = new ArrayList<>(32);\n\n        for (String inputValue : inputValues) {\n            if (verbose) {\n                LOG.info(\"Checking for input: {}\", inputValue);\n            }\n\n            TestRecord result = parse(inputValue);\n\n            if (verbose) {\n                LOG.info(\"Parse completed successfully\");\n            }\n\n            int longestFieldName = 0;\n            Set<String> allFieldNames = new HashSet<>();\n            allFieldNames.addAll(expectedStrings.keySet());\n            allFieldNames.addAll(expectedLongs.keySet());\n            allFieldNames.addAll(expectedDoubles.keySet());\n            allFieldNames.addAll(expectedValuePresent);\n            for (String key : allFieldNames) {\n                longestFieldName = Math.max(longestFieldName, key.length());\n            }\n\n            for (Map.Entry<String, String> expectation : expectedStrings.entrySet()) {\n                String fieldName = expectation.getKey();\n                if (!result.hasStringValue(fieldName)) {\n                    expectationResults.add(new ExpectationResult(\"String value\", fieldName, expectation.getValue(), \"Missing\"));\n                } else {\n                    expectEquals(expectationResults, fieldName, \"String value\",\n                        expectation.getValue(), result.getStringValue(fieldName));\n                }\n                if (verbose) {\n                    LOG.info(\"Passed: String value for '{}'{} was correctly : {}\",\n                        fieldName, padding(fieldName, longestFieldName), result.getStringValue(fieldName));\n                }\n            }\n\n            for (Map.Entry<String, Long> expectation : expectedLongs.entrySet()) {\n                String fieldName = expectation.getKey();\n                if (!result.hasLongValue(fieldName)) {\n                    expectationResults.add(new ExpectationResult(\"Long value\", fieldName, expectation.getValue(), \"Missing\"));\n                } else {\n                    expectEquals(expectationResults, fieldName, \"Long value\",\n                        expectation.getValue(), result.getLongValue(fieldName));\n                }\n                if (verbose) {\n                    LOG.info(\"Passed: Long   value for '{}'{} was correctly : {}\",\n                        fieldName, padding(fieldName, longestFieldName), result.getLongValue(fieldName));\n                }\n            }\n\n            for (Map.Entry<String, Double> expectation : expectedDoubles.entrySet()) {\n                String fieldName = expectation.getKey();\n                if (!result.hasDoubleValue(fieldName)) {\n                    expectationResults.add(new ExpectationResult(\"Double value\", fieldName, expectation.getValue(), \"Missing\"));\n                } else {\n                    expectEquals(expectationResults, fieldName, \"Double value\",\n                        expectation.getValue(), result.getDoubleValue(fieldName));\n                }\n                if (verbose) {\n                    LOG.info(\"Passed: Double value for '{}'{} was correctly : {}\",\n                        fieldName, padding(fieldName, longestFieldName), result.getDoubleValue(fieldName));\n                }\n            }\n\n            for (String fieldName: expectedValuePresent) {\n                expectationResults.add(new ExpectationResult(\"String present\", fieldName, null, result.hasStringValue(fieldName) ? null : \"Missing\"));\n\n                if (verbose) {\n                    LOG.info(\"Passed: A value for '{}'{} was present.\", fieldName, padding(fieldName, longestFieldName));\n                }\n            }\n\n        }\n        return expectationResults;\n    }\n\n    private List<ExpectationResult> checkExpectedAbsent() {\n        if (expectedAbsentStrings.size() +\n            expectedAbsentLongs.size() +\n            expectedAbsentDoubles.size() == 0) {\n            return Collections.emptyList(); // Nothing to do here\n        }\n\n        if (inputValues.isEmpty()) {\n            fail(\"No inputvalues were specified\");\n        }\n\n        List<ExpectationResult> expectationResults = new ArrayList<>(32);\n\n        for (String inputValue : inputValues) {\n            if (verbose) {\n                LOG.info(\"Checking for input: {}\", inputValue);\n            }\n\n            TestRecord result = parse(inputValue);\n\n            if (verbose) {\n                LOG.info(\"Parse completed successfully\");\n            }\n\n            int longestFieldName = 0;\n            Set<String> allFieldNames = new HashSet<>();\n            allFieldNames.addAll(expectedAbsentStrings);\n            allFieldNames.addAll(expectedAbsentLongs);\n            allFieldNames.addAll(expectedAbsentDoubles);\n            allFieldNames.addAll(expectedValuePresent);\n            for (String key : allFieldNames) {\n                longestFieldName = Math.max(longestFieldName, key.length());\n            }\n\n            for (String fieldName: expectedAbsentStrings) {\n                expectationResults.add(new ExpectationResult(\"String absent\", fieldName,\n                    result.getStringValue(fieldName), result.hasStringValue(fieldName) ? \"Present\" : null));\n                if (verbose) {\n                    LOG.info(\"Passed: String value for '{}'{} was correctly absent\", fieldName, padding(fieldName, longestFieldName));\n                }\n            }\n\n            for (String fieldName: expectedAbsentLongs) {\n                expectationResults.add(new ExpectationResult(\"Long absent\", fieldName,\n                    result.getLongValue(fieldName), result.hasLongValue(fieldName) ? \"Present\" : null));\n                if (verbose) {\n                    LOG.info(\"Passed: Long value for '{}'{} was correctly absent\", fieldName, padding(fieldName, longestFieldName));\n                }\n            }\n\n            for (String fieldName: expectedAbsentDoubles) {\n                expectationResults.add(new ExpectationResult(\"Double absent\", fieldName,\n                    result.getDoubleValue(fieldName), result.hasDoubleValue(fieldName) ? \"Present\" : null));\n                if (verbose) {\n                    LOG.info(\"Passed: Double value for '{}'{} was correctly absent\", fieldName, padding(fieldName, longestFieldName));\n                }\n            }\n        }\n        return expectationResults;\n    }\n\n    private TestRecord parse(String inputValue) {\n        TestRecord testRecord = new TestRecord();\n        if (verbose) {\n            testRecord.setVerbose();\n        }\n        try {\n            return parser.parse(testRecord, inputValue);\n        } catch (DissectionFailure | InvalidDissectorException | MissingDissectorsException e) {\n            fail(e.toString());\n        }\n        return testRecord; // This will never happen\n    }\n\n    private List<ExpectationResult> checkExpectedPossible() {\n        if (expectedPossible.isEmpty()) {\n            return Collections.emptyList();\n        }\n        List<ExpectationResult> expectationResults = new ArrayList<>(32);\n\n        int longestFieldName = 0;\n        for (String fieldName : expectedPossible) {\n            longestFieldName = Math.max(longestFieldName, fieldName.length());\n        }\n\n        List<String> allpossible = parser.getPossiblePaths();\n        for (String fieldName: expectedPossible) {\n            expectationResults.add(new ExpectationResult(\"Fieldname possible\", fieldName,\n                null, allpossible.contains(fieldName) ? null : \"Not possible\"));\n            if (verbose) {\n                LOG.info(\"Passed: Fieldname '{}'{} is possible.\", fieldName, padding(fieldName, longestFieldName));\n            }\n        }\n        return expectationResults;\n    }\n\n    private List<ExpectationResult> checkDissectors() {\n        List<ExpectationResult> results = new ArrayList<>();\n        Set<Dissector> dissectors = parser.getAllDissectors();\n        for (Dissector dissector: dissectors) {\n            for (String output: dissector.getPossibleOutput()) {\n//                String baseMsg = \"Dissector \" + dissector.getClass().getSimpleName() + \" outputs \" + output;\n                String[] splitOutput = output.split(\":\", 2);\n                if (!splitOutput[0].toUpperCase(Locale.ENGLISH).equals(splitOutput[0])) {\n                    results.add(new ExpectationResult(\"Dissector input type is UPPERcase\",\n                        dissector.getClass().getSimpleName() + \" --> \" + output,\n                        splitOutput[0].toUpperCase(Locale.ENGLISH),\n                        \"\\\"\" + splitOutput[0] + \"\\\" is not fully uppercase.\"));\n                }\n\n                if (!splitOutput[1].toLowerCase(Locale.ENGLISH).equals(splitOutput[1])) {\n                    results.add(new ExpectationResult(\"Dissector output name is lowercase\",\n                        dissector.getClass().getSimpleName() + \" --> \" + output,\n                        splitOutput[1].toLowerCase(Locale.ENGLISH),\n                        \"\\\"\" + splitOutput[1] + \"\\\" is not fully uppercase.\"));\n                }\n            }\n            assertNotNull(\n                dissector.prepareForDissect(\"Checking for non-existing input handling\",\n                                            \"Checking for non-existing output handling\"),\n                \"Dissector::prepareForDissect may NEVER return null!!\");\n        }\n        return results;\n    }\n\n    private String centerPadding(String name, char center, int longestLeftSide) {\n        return padding(name.substring(0, name.indexOf(center)), longestLeftSide);\n    }\n\n    private String padding(String name, int longestFieldName) {\n        return padding(name, longestFieldName, ' ');\n    }\n\n    private String padding(String name, int longestFieldName, char pad) {\n        int length = longestFieldName - name.length();\n        if (length <= 0) {\n            return \"\";\n        }\n        StringBuilder sb = new StringBuilder(length);\n        for (int i = 0; i < length; i++) {\n            sb.append(pad);\n        }\n        return sb.toString();\n    }\n\n\n    public DissectorTester printDissectors() {\n        LOG.info(\"=====================================================\");\n        LOG.info(\"Dissectors:\");\n        LOG.info(\"=====================================================\");\n\n        Set<Dissector> dissectors = parser.getAllDissectors();\n        for (Dissector dissector: dissectors) {\n            LOG.info(\"-----------------------------------------------------\");\n            LOG.info(\"{} --> {}\", dissector.getInputType(), dissector.getClass().getSimpleName());\n            for (String output: dissector.getPossibleOutput()) {\n                LOG.info(\">> {}\", output);\n            }\n        }\n        LOG.info(\"=====================================================\");\n        return this;\n    }\n\n    public DissectorTester printPossible() {\n        LOG.info(\"=====================================================\");\n        LOG.info(\"Possible:\");\n        LOG.info(\"----------\");\n        for (String path: parser.getPossiblePaths()) {\n            LOG.info(\"---> {}\", path);\n        }\n        LOG.info(\"=====================================================\");\n        return this;\n    }\n\n    public List<String> getPossible() {\n        return parser.getPossiblePaths();\n    }\n\n    public DissectorTester printAllPossibleValues() {\n        if (inputValues.isEmpty()) {\n            fail(\"No inputvalues were specified\");\n        }\n\n        try {\n            List<String> possibleFieldNames = parser\n                .getPossiblePaths()\n                .stream()\n                .sorted(Comparator.comparing(o -> o.substring(o.indexOf(\":\"))))\n                .collect(Collectors.toList());\n\n            for (String path: possibleFieldNames) {\n                parser.addParseTarget(TestRecord.class.getMethod(\"setStringValue\", String.class, String.class), path);\n            }\n\n            for (String inputValue : inputValues) {\n                LOG.info(\"=====================================================\");\n                LOG.info(\"All values (except wildcards) for input:{}\", inputValue);\n                LOG.info(\"=====================================================\");\n                for (String path : possibleFieldNames) {\n                    parser.addParseTarget(TestRecord.class.getMethod(\"setStringValue\", String.class, String.class), path);\n                }\n                TestRecord result = parser.parse(inputValue);\n\n                int longestFieldName = 0;\n                int longestTypeIndicator = 0;\n                for (String fieldName : possibleFieldNames) {\n                    longestFieldName = Math.max(longestFieldName, fieldName.substring(fieldName.indexOf(\":\")).length());\n                    longestTypeIndicator = Math.max(longestTypeIndicator, fieldName.indexOf(\":\"));\n                }\n                for (String fieldName : result.getAllNames()) {\n                    longestFieldName = Math.max(longestFieldName, fieldName.substring(fieldName.indexOf(\":\")).length());\n                }\n                longestFieldName += 4;\n                for (String fieldName : possibleFieldNames) {\n                    String value = result.getStringValue(fieldName);\n                    if (value == null) {\n                        String wildcardEnd = \".*\";\n                        if (fieldName.endsWith(wildcardEnd)) {\n                            String fieldNamePrefix = fieldName.substring(0, fieldName.length() - wildcardEnd.length());\n                            List<String> allNames = result.getAllNames().stream()\n                                .filter(f -> f.startsWith(fieldNamePrefix))\n                                .filter(f -> !f.substring(fieldNamePrefix.length()+1).contains(\".\"))\n                                .sorted()\n                                .collect(Collectors.toList());\n\n                            String leftPadding = centerPadding(fieldName, ':', longestTypeIndicator);\n                            String rightPadding = padding(fieldName.substring(fieldName.indexOf(\":\")), longestFieldName);\n                            if (allNames.isEmpty()) {\n                                LOG.info(\"Found values for {}{}{}  = []\", leftPadding, fieldName, rightPadding);\n                            } else {\n                                LOG.info(\"Found values for {}{}\", leftPadding, fieldName);\n                                for (String name : allNames) {\n                                    leftPadding = centerPadding(name, ':', longestTypeIndicator);\n                                    rightPadding = padding(name.substring(name.indexOf(\":\")), longestFieldName);\n                                    LOG.info(\"             --> {}{}{}  = {}\", leftPadding, name, rightPadding, result.getStringValue(name));\n                                }\n                            }\n                            continue;\n                        } else {\n                            value = \"<<<null>>>\";\n                        }\n                    }\n                    String leftPadding = centerPadding(fieldName, ':', longestTypeIndicator);\n                    String rightPadding = padding(fieldName.substring(fieldName.indexOf(\":\")), longestFieldName);\n                    LOG.info(\"Found value for  {}{}{}  = {}\", leftPadding, fieldName, rightPadding, value);\n                }\n\n            }\n            LOG.info(\"=====================================================\");\n        } catch (NoSuchMethodException | InvalidDissectorException | MissingDissectorsException | DissectionFailure e) {\n            e.printStackTrace();\n            fail(\"Shouldn't have any exceptions\");\n        }\n        return this;\n    }\n\n    public DissectorTester printSeparator() {\n        LOG.info(\"\");\n        LOG.info(\"--------------------------------------------------------------------------------\");\n        LOG.info(\"\");\n        return this;\n    }\n\n    public static class DummyDissector extends Dissector {\n\n        private String outputType;\n        private String fieldName;\n\n        public DummyDissector() {\n        }\n\n        public DummyDissector(String newOutputType, String newFieldName) {\n            fieldName = newFieldName;\n            outputType = newOutputType;\n        }\n\n        @Override\n        public void dissect(Parsable<?> parsable, String inputname) throws DissectionFailure {\n            final ParsedField field = parsable.getParsableField(\"DUMMYROOT\", inputname);\n            parsable.addDissection(inputname, outputType, fieldName, field.getValue());\n        }\n\n        @Override\n        public String getInputType() {\n            return \"DUMMYROOT\";\n        }\n\n        @Override\n        public List<String> getPossibleOutput() {\n            return Collections.singletonList(outputType + \":\" + fieldName);\n        }\n\n        @Override\n        public EnumSet<Casts> prepareForDissect(String inputname, String outputname) {\n            return STRING_ONLY;\n        }\n\n        @Override\n        protected void initializeNewInstance(Dissector newInstance) {\n            DummyDissector dummyDissector = (DummyDissector)newInstance;\n            dummyDissector.fieldName = fieldName;\n            dummyDissector.outputType = outputType;\n        }\n    }\n}\n"
  },
  {
    "path": "parser-core/src/test/java/nl/basjes/parse/core/test/EmptyValuesDissector.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.core.test;\n\nimport nl.basjes.parse.core.Parsable;\nimport nl.basjes.parse.core.Value;\nimport nl.basjes.parse.core.exceptions.DissectionFailure;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class EmptyValuesDissector extends UltimateDummyDissector {\n    private static final Logger LOG = LoggerFactory.getLogger(EmptyValuesDissector.class);\n\n    public EmptyValuesDissector() {\n    }\n\n    public EmptyValuesDissector(String inputType) {\n        super(inputType);\n    }\n\n    @Override\n    public void dissect(Parsable<?> parsable, String inputname, Value value) throws DissectionFailure {\n        LOG.info(\"Outputting \\\"EMPTY\\\" values\");\n        parsable\n            .addDissection(inputname, \"ANY\",    \"any\",    \"\")\n            .addDissection(inputname, \"STRING\", \"string\", \"\")\n            .addDissection(inputname, \"INT\",    \"int\",    \"\")\n            .addDissection(inputname, \"LONG\",   \"long\",   \"\")\n            .addDissection(inputname, \"FLOAT\",  \"float\",  \"\")\n            .addDissection(inputname, \"DOUBLE\", \"double\", \"\");\n    }\n}\n"
  },
  {
    "path": "parser-core/src/test/java/nl/basjes/parse/core/test/MyDissectorTester.java",
    "content": "/*\n * Apache HTTPD logparsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage nl.basjes.parse.core.test;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\npublic class MyDissectorTester {\n    @Test\n    void testPrefixInserter(){\n        DissectorTester tester = DissectorTester.create();\n\n        tester.withPathPrefix(\"Prefix.\");\n        assertEquals(\"Foo:Prefix.Bar\", tester.addPrefix(\"Foo:Bar\"));\n\n        tester.withPathPrefix(\"\");\n        assertEquals(\"Foo:Bar\", tester.addPrefix(\"Foo:Bar\"));\n\n        tester.withPathPrefix(\"Prefix.\");\n        assertEquals(\"Foo:Prefix.Bar\", tester.addPrefix(\"Foo:Bar\"));\n\n        tester.withPathPrefix(null);\n        assertEquals(\"Foo:Bar\", tester.addPrefix(\"Foo:Bar\"));\n\n        tester.withPathPrefix(\"Prefix.\");\n        assertEquals(\"Foo:Prefix.Bar\", tester.addPrefix(\"Foo:Bar\"));\n    }\n\n}\n"
  },
  {
    "path": "parser-core/src/test/java/nl/basjes/parse/core/test/NormalValuesDissector.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.core.test;\n\nimport nl.basjes.parse.core.Parsable;\nimport nl.basjes.parse.core.Value;\nimport nl.basjes.parse.core.exceptions.DissectionFailure;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class NormalValuesDissector extends UltimateDummyDissector {\n    private static final Logger LOG = LoggerFactory.getLogger(NormalValuesDissector.class);\n\n    public NormalValuesDissector() {\n    }\n\n    public NormalValuesDissector(String inputType) {\n        super(inputType);\n    }\n\n    @Override\n    public void dissect(Parsable<?> parsable, String inputname, Value value) throws DissectionFailure {\n        LOG.info(\"Outputting \\\"NORMAL\\\" values\");\n        parsable\n            .addDissection(inputname, \"ANY\",    \"any\",    \"42\")\n            .addDissection(inputname, \"STRING\", \"string\", \"FortyTwo\")\n            .addDissection(inputname, \"INT\",    \"int\",    42)\n            .addDissection(inputname, \"LONG\",   \"long\",   42L)\n            .addDissection(inputname, \"FLOAT\",  \"float\",  42F)\n            .addDissection(inputname, \"DOUBLE\", \"double\", 42D);\n    }\n}\n"
  },
  {
    "path": "parser-core/src/test/java/nl/basjes/parse/core/test/NullValuesDissector.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.core.test;\n\nimport nl.basjes.parse.core.Parsable;\nimport nl.basjes.parse.core.Value;\nimport nl.basjes.parse.core.exceptions.DissectionFailure;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class NullValuesDissector extends UltimateDummyDissector {\n    private static final Logger LOG = LoggerFactory.getLogger(NullValuesDissector.class);\n\n    public NullValuesDissector() {\n    }\n\n    public NullValuesDissector(String inputType) {\n        super(inputType);\n    }\n\n    @Override\n    public void dissect(Parsable<?> parsable, String inputname, Value value) throws DissectionFailure {\n        LOG.info(\"Outputting \\\"NULL\\\" values\");\n        parsable\n            .addDissection(inputname, \"ANY\",    \"any\",    (String) null)\n            .addDissection(inputname, \"STRING\", \"string\", (String) null)\n            .addDissection(inputname, \"INT\",    \"int\",    (Long)   null)\n            .addDissection(inputname, \"LONG\",   \"long\",   (Long)   null)\n            .addDissection(inputname, \"FLOAT\",  \"float\",  (Double) null)\n            .addDissection(inputname, \"DOUBLE\", \"double\", (Double) null);\n    }\n}\n"
  },
  {
    "path": "parser-core/src/test/java/nl/basjes/parse/core/test/TestRecord.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.core.test;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.TreeSet;\n\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\nimport static org.junit.jupiter.api.Assertions.assertNull;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.api.Assertions.fail;\n\npublic class TestRecord {\n\n    private static final Logger LOG = LoggerFactory.getLogger(DissectorTester.class);\n\n    private final Map<String, Set<String>>  stringMap   = new HashMap<>(32);\n    private final Map<String, Set<Long>>    longMap     = new HashMap<>(32);\n    private final Map<String, Set<Double>>  doubleMap   = new HashMap<>(32);\n\n    public void setVerbose() {\n        this.verbose = true;\n    }\n\n    boolean verbose = false;\n\n    public Set<String> getAllNames() {\n        Set<String> allNames = new TreeSet<>();\n        allNames.addAll(stringMap.keySet());\n        allNames.addAll(longMap.keySet());\n        allNames.addAll(doubleMap.keySet());\n        return allNames;\n    }\n\n    public void setStringValue(final String name, final String value) {\n        if (verbose) {\n            LOG.info(\"Received String: {} = {}\", name, value);\n        }\n        stringMap.computeIfAbsent(name, s -> new HashSet<>()).add(value);\n    }\n\n    public void setLongValue(final String name, final Long value) {\n        if (verbose) {\n            LOG.info(\"Received Long  : {} = {}\", name, value);\n        }\n        longMap.computeIfAbsent(name, s -> new HashSet<>()).add(value);\n    }\n\n    public void setDoubleValue(final String name, final Double value) {\n        if (verbose) {\n            LOG.info(\"Received Double: {} = {}\", name, value);\n        }\n        doubleMap.computeIfAbsent(name, s -> new HashSet<>()).add(value);\n    }\n\n    public String  getStringValue(final String name) {\n        Set<String> value = stringMap.get(name);\n        if (value == null) {\n            return null;\n        }\n        return value.iterator().next();\n    }\n\n    public Long    getLongValue(final String name) {\n        Set<Long> value = longMap.get(name);\n        if (value == null) {\n            return null;\n        }\n        return value.iterator().next();\n    }\n\n    public Double  getDoubleValue(final String name) {\n        Set<Double> value = doubleMap.get(name);\n        if (value == null) {\n            return null;\n        }\n        return value.iterator().next();\n    }\n\n    public Set<String>  getStringValues(final String name) {\n        return stringMap.get(name);\n    }\n\n    public Set<Long>    getLongValues(final String name) {\n        return longMap.get(name);\n    }\n\n    public Set<Double>  getDoubleValues(final String name) {\n        return doubleMap.get(name);\n    }\n\n    public boolean hasStringValue(final String name) {\n        return stringMap.containsKey(name);\n    }\n\n    public boolean hasLongValue(final String name) {\n        return longMap.containsKey(name);\n    }\n\n    public boolean hasDoubleValue(final String name) {\n        return doubleMap.containsKey(name);\n    }\n\n    public TestRecord expectString(String field, String... values) {\n        if (values == null){\n            isPresent(stringMap, field, values);\n        } else {\n            for (String value : values) {\n                isPresent(stringMap, field, value);\n            }\n        }\n        return this;\n    }\n\n    public TestRecord expectLong(String field, Long... values) {\n        if (values == null){\n            isPresent(longMap, field, values);\n        } else {\n            for (Long value : values) {\n                isPresent(longMap, field, value);\n            }\n        }\n        return this;\n    }\n\n    public TestRecord expectDouble(String field, Double... values) {\n        if (values == null){\n            isPresent(doubleMap, field, values);\n        } else {\n            for (Double value : values) {\n                isPresent(doubleMap, field, value);\n            }\n        }\n        return this;\n    }\n\n    private void isPresent(Map<String, ?> results, String field, Object value) {\n        if (value == null) {\n            assertTrue(results.containsKey(field), \"The field \\\"\"+field+\"\\\" is missing (a null value was expected).\");\n            Object actualValue = results.get(field);\n            assertNotNull(actualValue, \"The field \\\"\" + field + \"\\\" should be present but it is not\");\n            assertTrue(actualValue instanceof Set, \"Invalid type used, result must be a Set<?>\");\n            Set<?> actualValues = (Set<?>)actualValue;\n            for (Object actualValuee: actualValues) {\n                assertNull(actualValuee, \"The field \\\"\" + field + \"\\\" should only have null values but we found: \" +\n                    \"(\" + actualValue.getClass().getSimpleName() + \")\\\"\" + actualValue + \"\\\" \");\n            }\n        } else {\n            assertTrue(results.containsKey(field),\n                \"The field \\\"\"+field+\"\\\" is missing (an entry of type \"+value.getClass().getSimpleName()+\" was expected).\");\n            Object result = results.get(field);\n            assertTrue(result instanceof Set, \"Invalid type used, result must be a Set<?>\");\n            Set<?> resultSet = (Set<?>)result;\n            assertTrue(resultSet.contains(value),\n                \"The field \\\"\" + field + \"\\\" should have the value (\" + value + \")\\\"\" + value + \"\\\"is missing\");\n        }\n    }\n\n    public TestRecord noString(String field) {\n        isAbsent(stringMap, field);\n        return this;\n    }\n\n    public TestRecord noLong(String field) {\n        isAbsent(longMap, field);\n        return this;\n    }\n\n    public TestRecord noDouble(String field) {\n        isAbsent(doubleMap, field);\n        return this;\n    }\n\n    private void isAbsent(Map<String, ?> results, String field) {\n        Object value = results.get(field);\n        if (value != null) {\n            fail(\"The value \\\"\"+value+\"\\\" was found for field \\\"\"+field+\"\\\"\");\n        } else {\n            assertFalse(results.containsKey(field) || results.get(field) != null,\n                \"A null value was found for field \\\"\" + field + \"\\\"\");\n        }\n    }\n\n    public void clear() {\n        stringMap.clear();\n        longMap.clear();\n        doubleMap.clear();\n    }\n}\n"
  },
  {
    "path": "parser-core/src/test/java/nl/basjes/parse/core/test/TestUltimateDummyDissector.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.core.test;\n\nimport org.junit.jupiter.api.Test;\n\nclass TestUltimateDummyDissector {\n\n    @Test\n    void verifyUltimateDummyDissector() {\n        DissectorTester.create()\n            .withDissector(new NormalValuesDissector())\n            .withInput(\"Doesn't matter\")\n            .expect(\"ANY:any\",        \"42\")\n            .expect(\"ANY:any\",        42L)\n            .expect(\"ANY:any\",        42D)\n            .expect(\"STRING:string\",  \"FortyTwo\")\n            .expectAbsentLong(\"STRING:string\")\n            .expectAbsentDouble(\"STRING:string\")\n            .expect(\"INT:int\",        \"42\")\n            .expect(\"INT:int\",        42L)\n            .expectAbsentDouble(\"INT:int\")\n            .expect(\"LONG:long\",      \"42\")\n            .expect(\"LONG:long\",      42L)\n            .expectAbsentDouble(\"LONG:long\")\n            .expect(\"FLOAT:float\",    \"42.0\")\n            .expectAbsentLong(\"FLOAT:float\")\n            .expect(\"FLOAT:float\",    42D)\n            .expect(\"DOUBLE:double\",  \"42.0\")\n            .expectAbsentLong(\"DOUBLE:double\")\n            .expect(\"DOUBLE:double\",  42D)\n//            .verbose()\n            .checkExpectations();\n    }\n\n}\n"
  },
  {
    "path": "parser-core/src/test/java/nl/basjes/parse/core/test/TestUltimateDummyDissectorFailurelogging.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.core.test;\n\nimport org.junit.jupiter.api.Test;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\n\nclass TestUltimateDummyDissectorFailurelogging {\n\n    @Test\n    void verifyErrorSituation() {\n        AssertionError assertionError = assertThrows(AssertionError.class, () ->\n            DissectorTester.create()\n                .withDissector(new NormalValuesDissector())\n                .withInput(\"Doesn't matter\")\n                // All good\n                .expect(\"ANY:any\", \"42\")\n                .expect(\"ANY:any\", 42L)\n                .expect(\"ANY:any\", 42D)\n                .expect(\"STRING:string\", \"FortyTwo\")\n                .expectAbsentLong(\"STRING:string\")\n                .expectAbsentDouble(\"STRING:string\")\n                .expect(\"INT:int\", \"42\")\n                .expect(\"INT:int\", 42L)\n                .expectAbsentDouble(\"INT:int\")\n                .expect(\"LONG:long\", \"42\")\n                .expect(\"LONG:long\", 42L)\n                .expectAbsentDouble(\"LONG:long\")\n                .expect(\"FLOAT:float\", \"42.0\")\n                .expectAbsentLong(\"FLOAT:float\")\n                .expect(\"FLOAT:float\", 42D)\n                .expect(\"DOUBLE:double\", \"42.0\")\n                .expectAbsentLong(\"DOUBLE:double\")\n                .expect(\"DOUBLE:double\", 42D)\n\n                // All bad\n                .expect(\"ANY:any\", \"43\")\n                .expect(\"ANY:any\", 43L)\n                .expect(\"ANY:any\", 43D)\n                .expect(\"STRING:string\", \"FortyThree\")\n                .expectAbsentString(\"STRING:string\")\n                .expect(\"INT:int\", \"43\")\n                .expect(\"INT:int\", 43L)\n                .expectAbsentLong(\"INT:int\")\n                .expect(\"LONG:long\", \"43\")\n                .expect(\"LONG:long\", 43L)\n                .expectAbsentLong(\"LONG:long\")\n                .expect(\"FLOAT:float\", \"43.0\")\n                .expectAbsentDouble(\"FLOAT:float\")\n                .expect(\"FLOAT:float\", 43D)\n                .expect(\"DOUBLE:double\", \"43.0\")\n                .expectAbsentDouble(\"DOUBLE:double\")\n                .expect(\"DOUBLE:double\", 43D)\n                .verbose()\n                .checkExpectations()\n        );\n\n        assertEquals(\"\\n\" +\n            \"[     ] /========================================================================\\\\\\n\"+\n            \"[     ] | Field         | Check         | Expected Value | Fail reason           |\\n\"+\n            \"[     ] +---------------+---------------+----------------+-----------------------+\\n\"+\n            \"[ERROR] | ANY:any       | String value  |             43 | Wrong value: 42       |\\n\" +\n            \"[ERROR] | DOUBLE:double | String value  |           43.0 | Wrong value: 42.0     |\\n\" +\n            \"[ERROR] | FLOAT:float   | String value  |           43.0 | Wrong value: 42.0     |\\n\" +\n            \"[ERROR] | INT:int       | String value  |             43 | Wrong value: 42       |\\n\" +\n            \"[ERROR] | LONG:long     | String value  |             43 | Wrong value: 42       |\\n\" +\n            \"[ERROR] | STRING:string | String value  |     FortyThree | Wrong value: FortyTwo |\\n\" +\n            \"[ERROR] | ANY:any       | Long value    |             43 | Wrong value: 42       |\\n\" +\n            \"[ERROR] | INT:int       | Long value    |             43 | Wrong value: 42       |\\n\" +\n            \"[ERROR] | LONG:long     | Long value    |             43 | Wrong value: 42       |\\n\" +\n            \"[ERROR] | ANY:any       | Double value  |           43.0 | Wrong value: 42.0     |\\n\" +\n            \"[ERROR] | DOUBLE:double | Double value  |           43.0 | Wrong value: 42.0     |\\n\" +\n            \"[ERROR] | FLOAT:float   | Double value  |           43.0 | Wrong value: 42.0     |\\n\" +\n            \"[ERROR] | STRING:string | String absent |       FortyTwo | Present               |\\n\" +\n            \"[     ] | STRING:string | Long absent   |                |                       |\\n\" +\n            \"[     ] | FLOAT:float   | Long absent   |                |                       |\\n\" +\n            \"[     ] | DOUBLE:double | Long absent   |                |                       |\\n\" +\n            \"[ERROR] | INT:int       | Long absent   |             42 | Present               |\\n\" +\n            \"[ERROR] | LONG:long     | Long absent   |             42 | Present               |\\n\" +\n            \"[     ] | STRING:string | Double absent |                |                       |\\n\" +\n            \"[     ] | INT:int       | Double absent |                |                       |\\n\" +\n            \"[     ] | LONG:long     | Double absent |                |                       |\\n\" +\n            \"[ERROR] | FLOAT:float   | Double absent |           42.0 | Present               |\\n\" +\n            \"[ERROR] | DOUBLE:double | Double absent |           42.0 | Present               |\\n\" +\n            \"[     ] \\\\========================================================================/\\n\",\n            assertionError.getMessage());\n    }\n\n}\n"
  },
  {
    "path": "parser-core/src/test/java/nl/basjes/parse/core/test/UltimateDummyDissector.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.core.test;\n\nimport nl.basjes.parse.core.Casts;\nimport nl.basjes.parse.core.SimpleDissector;\n\nimport java.util.EnumSet;\nimport java.util.HashMap;\n\nimport static nl.basjes.parse.core.Casts.STRING_ONLY;\nimport static nl.basjes.parse.core.Casts.STRING_OR_DOUBLE;\nimport static nl.basjes.parse.core.Casts.STRING_OR_LONG;\nimport static nl.basjes.parse.core.Casts.STRING_OR_LONG_OR_DOUBLE;\n\n/**\n * A dummy dissector to ensure retrieving all types works in the various wrappers\n */\npublic abstract class UltimateDummyDissector extends SimpleDissector {\n\n    private static final HashMap<String, EnumSet<Casts>> DISSECTOR_CONFIG = new HashMap<>();\n    static {\n        DISSECTOR_CONFIG.put(\"ANY:any\",         STRING_OR_LONG_OR_DOUBLE);\n        DISSECTOR_CONFIG.put(\"STRING:string\",   STRING_ONLY);\n        DISSECTOR_CONFIG.put(\"INT:int\",         STRING_OR_LONG);\n        DISSECTOR_CONFIG.put(\"LONG:long\",       STRING_OR_LONG);\n        DISSECTOR_CONFIG.put(\"FLOAT:float\",     STRING_OR_DOUBLE);\n        DISSECTOR_CONFIG.put(\"DOUBLE:double\",   STRING_OR_DOUBLE);\n    }\n\n    public UltimateDummyDissector() {\n        super(\"INPUT\", DISSECTOR_CONFIG);\n    }\n\n    public UltimateDummyDissector(String inputType) {\n        super(inputType, DISSECTOR_CONFIG);\n    }\n\n    @Override\n    public boolean initializeFromSettingsParameter(String settings) {\n        setInputType(settings);\n        return true;\n    }\n\n\n}\n"
  },
  {
    "path": "parser-core/src/test/resources/log4j.properties",
    "content": "#\n# Apache HTTPD & NGINX Access log parsing made easy\n# Copyright (C) 2011-2023 Niels Basjes\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# Root logger option\nlog4j.rootLogger=DEBUG, stdout\n#, file\nlog4j.appender.stdout=org.apache.log4j.ConsoleAppender\nlog4j.appender.stdout.Target=System.out\nlog4j.appender.stdout.threshold=INFO\nlog4j.appender.stdout.layout=org.apache.log4j.PatternLayout\nlog4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} [%-5p] %-40c{1}:%5L: %m%n\n\n## file appender\n#log4j.appender.file=org.apache.log4j.RollingFileAppender\n#log4j.appender.file.File=target/debug.log\n#log4j.appender.file.threshold=DEBUG\n#log4j.appender.file.layout=org.apache.log4j.PatternLayout\n#log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd} %d{ABSOLUTE} [%-5p] %-40c{1}:%5L: %m%n\n#log4j.appender.file.Append=false\n"
  },
  {
    "path": "pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n Apache HTTPD & NGINX Access log parsing made easy\n Copyright (C) 2011-2023 Niels Basjes\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n https://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n-->\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n  <modelVersion>4.0.0</modelVersion>\n  <groupId>nl.basjes.parse</groupId>\n  <artifactId>parser-parent</artifactId>\n  <version>6.0.1-SNAPSHOT</version>\n  <packaging>pom</packaging>\n  <name>Parser -</name>\n  <description>A library to allow easy parsing of Apache HTTPD access logs with Java.</description>\n  <url>https://github.com/nielsbasjes/logparser</url>\n\n  <properties>\n    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n    <project.build.outputEncoding>UTF-8</project.build.outputEncoding>\n    <maven.compiler.source>21</maven.compiler.source>\n    <maven.compiler.release>21</maven.compiler.release>\n\n    <project.build.outputTimestamp>2025-12-26T11:16:47Z</project.build.outputTimestamp>\n\n    <!-- In order for everything to work correctly we need atleast this version of maven. -->\n    <maven.minimal.version>3.9.11</maven.minimal.version>\n\n    <depencency-convergence.phase>validate</depencency-convergence.phase>\n\n    <checkstyle-plugin.version>3.6.0</checkstyle-plugin.version>\n    <checkstyle.version>13.4.2</checkstyle.version>\n\n    <lombok.version>1.18.46</lombok.version>\n    <slf4j.version>2.0.18</slf4j.version>\n\n    <junit5.version>6.0.3</junit5.version>\n    <hamcrest-core.version>3.0</hamcrest-core.version>\n\n    <hadoop.version>3.5.0</hadoop.version>\n    <hive.version>4.2.0</hive.version>\n    <avro.version>1.12.1</avro.version>\n    <beam.version>2.70.0</beam.version>\n    <flink.version>2.2.0</flink.version>\n\n    <rat.version>0.18</rat.version>\n    <jacoco-maven-plugin.version>0.8.14</jacoco-maven-plugin.version>\n\n    <!-- See http://www.eclemma.org/jacoco/trunk/doc/prepare-agent-mojo.html-->\n    <jacoco.surefireArgLine />\n    <argLine>@{jacoco.surefireArgLine} -Xmx2048m</argLine>\n  </properties>\n\n  <dependencyManagement>\n    <dependencies>\n      <dependency>\n        <groupId>org.junit</groupId>\n        <artifactId>junit-bom</artifactId>\n        <version>${junit5.version}</version>\n        <type>pom</type>\n        <scope>import</scope>\n      </dependency>\n    </dependencies>\n  </dependencyManagement>\n\n  <dependencies>\n\n    <dependency>\n      <groupId>org.slf4j</groupId>\n      <artifactId>slf4j-api</artifactId>\n      <version>${slf4j.version}</version>\n    </dependency>\n\n    <dependency>\n      <groupId>org.apache.commons</groupId>\n      <artifactId>commons-text</artifactId>\n      <version>1.15.0</version>\n    </dependency>\n\n    <dependency>\n      <groupId>org.junit.jupiter</groupId>\n      <artifactId>junit-jupiter-engine</artifactId>\n      <scope>test</scope>\n    </dependency>\n\n    <dependency>\n      <groupId>org.junit.jupiter</groupId>\n      <artifactId>junit-jupiter-params</artifactId>\n      <scope>test</scope>\n    </dependency>\n\n    <dependency>\n      <groupId>org.junit.vintage</groupId>\n      <artifactId>junit-vintage-engine</artifactId>\n      <scope>test</scope>\n      <exclusions>\n        <exclusion>\n          <groupId>org.hamcrest</groupId>\n          <artifactId>hamcrest-core</artifactId>\n        </exclusion>\n      </exclusions>\n    </dependency>\n\n    <dependency>\n      <groupId>org.hamcrest</groupId>\n      <artifactId>hamcrest-core</artifactId>\n      <version>${hamcrest-core.version}</version>\n      <scope>test</scope>\n    </dependency>\n\n    <dependency>\n      <groupId>org.slf4j</groupId>\n      <artifactId>slf4j-reload4j</artifactId>\n      <version>${slf4j.version}</version>\n      <scope>test</scope>\n    </dependency>\n\n  </dependencies>\n\n  <profiles>\n    <profile>\n      <id>release</id>\n      <build>\n        <plugins>\n\n          <plugin>\n            <groupId>org.apache.maven.plugins</groupId>\n            <artifactId>maven-gpg-plugin</artifactId>\n            <version>3.2.8</version>\n            <executions>\n              <execution>\n                <id>sign-artifacts</id>\n                <phase>verify</phase>\n                <goals>\n                  <goal>sign</goal>\n                </goals>\n                <configuration>\n                  <gpgArguments>\n                    <arg>--pinentry-mode</arg>\n                    <arg>loopback</arg>\n                  </gpgArguments>\n                </configuration>\n              </execution>\n            </executions>\n          </plugin>\n\n          <plugin>\n            <groupId>org.apache.maven.plugins</groupId>\n            <artifactId>maven-source-plugin</artifactId>\n            <version>3.4.0</version>\n            <executions>\n              <execution>\n                <id>attach-sources</id>\n                <goals>\n                  <goal>jar-no-fork</goal>\n                </goals>\n              </execution>\n            </executions>\n          </plugin>\n\n          <plugin>\n            <groupId>org.apache.maven.plugins</groupId>\n            <artifactId>maven-javadoc-plugin</artifactId>\n            <version>3.12.0</version>\n            <executions>\n              <execution>\n                <id>attach-javadocs</id>\n                <goals>\n                  <goal>jar</goal>\n                </goals>\n                <configuration>\n                  <!-- TODO: Remove workaround for issue https://github.com/apache/maven-javadoc-plugin/issues/1244 when fixed -->\n                  <disableNoFonts>true</disableNoFonts>\n                </configuration>\n              </execution>\n            </executions>\n          </plugin>\n\n          <!-- https://central.sonatype.org/publish/publish-portal-maven/ -->\n          <plugin>\n            <groupId>org.sonatype.central</groupId>\n            <artifactId>central-publishing-maven-plugin</artifactId>\n            <version>0.10.0</version>\n            <extensions>true</extensions>\n            <configuration>\n              <publishingServerId>central</publishingServerId>\n              <deploymentName>Logparser ${project.version}</deploymentName>\n              <!--              <autoPublish>true</autoPublish>-->\n              <!--              <waitUntil>published</waitUntil>-->\n              <waitUntil>validated</waitUntil>\n              <excludeArtifacts>\n                <!-- Devtools -->\n                <excludeArtifact>devtools</excludeArtifact>\n                <excludeArtifact>parse-utils</excludeArtifact>\n                <excludeArtifact>PojoGenerator</excludeArtifact>\n                <!-- Examples -->\n                <excludeArtifact>httpdlog-examples</excludeArtifact>\n                <excludeArtifact>java-pojo</excludeArtifact>\n                <excludeArtifact>apache-hadoop-mapreduce</excludeArtifact>\n                <excludeArtifact>apache-flink</excludeArtifact>\n                <excludeArtifact>apache-beam</excludeArtifact>\n              </excludeArtifacts>\n            </configuration>\n          </plugin>\n\n        </plugins>\n      </build>\n    </profile>\n\n    <profile>\n      <id>EnableReportPlugins</id>\n      <build>\n        <plugins>\n          <plugin>\n            <groupId>org.jacoco</groupId>\n            <artifactId>jacoco-maven-plugin</artifactId>\n          </plugin>\n        </plugins>\n      </build>\n    </profile>\n\n  </profiles>\n\n  <build>\n    <defaultGoal>clean package</defaultGoal>\n    <pluginManagement>\n      <plugins>\n        <plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-clean-plugin</artifactId><version>3.5.0</version></plugin>\n        <plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.15.0</version></plugin>\n        <plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-deploy-plugin</artifactId><version>3.1.4</version></plugin>\n        <plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-enforcer-plugin</artifactId><version>3.6.2</version></plugin>\n        <plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-install-plugin</artifactId><version>3.1.4</version></plugin>\n        <plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-jar-plugin</artifactId><version>3.5.0</version></plugin>\n        <plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-resources-plugin</artifactId><version>3.5.0</version></plugin>\n        <plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-shade-plugin</artifactId><version>3.6.2</version></plugin>\n        <plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-surefire-plugin</artifactId><version>3.5.5</version></plugin>\n        <plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-assembly-plugin</artifactId><version>3.8.0</version></plugin>\n        <plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-toolchains-plugin</artifactId><version>3.2.0</version></plugin>\n\n        <plugin>\n          <groupId>org.apache.maven.plugins</groupId>\n          <artifactId>maven-checkstyle-plugin</artifactId>\n          <version>${checkstyle-plugin.version}</version>\n          <dependencies>\n            <dependency>\n              <groupId>nl.basjes.parse.devtools</groupId>\n              <artifactId>devtools</artifactId>\n              <version>${project.version}</version>\n            </dependency>\n            <dependency>\n              <groupId>com.puppycrawl.tools</groupId>\n              <artifactId>checkstyle</artifactId>\n              <version>${checkstyle.version}</version>\n            </dependency>\n          </dependencies>\n          <configuration>\n            <consoleOutput>true</consoleOutput>\n            <configLocation>checkstyle/checkstyle.xml</configLocation>\n            <suppressionsLocation>checkstyle/suppressions.xml</suppressionsLocation>\n            <includeTestSourceDirectory>true</includeTestSourceDirectory>\n          </configuration>\n          <!-- Runs by default in the verify phase  (mvn verify or later in the build cycle)\n               the 'check' goal will fail the build if it does not pass.  \"mvn checkstyle:check\"\n               will do this alone, or \"mvn checkstyle:checkstyle\" will report but not break  -->\n          <executions>\n            <execution>\n              <id>checkstyle-check</id>\n              <phase>test</phase>\n              <goals>\n                <goal>check</goal>\n              </goals>\n            </execution>\n          </executions>\n        </plugin>\n\n        <!-- Coverage analysis for tests -->\n        <plugin>\n          <groupId>org.jacoco</groupId>\n          <artifactId>jacoco-maven-plugin</artifactId>\n          <version>${jacoco-maven-plugin.version}</version>\n          <executions>\n            <!--\n                Prepares the property pointing to the JaCoCo runtime agent which\n                is passed as VM argument when Maven the Surefire plugin is executed.\n            -->\n            <execution>\n              <id>pre-unit-test</id>\n              <goals>\n                <goal>prepare-agent</goal>\n              </goals>\n              <configuration>\n                <!--\n                    Sets the name of the property containing the settings\n                    for JaCoCo runtime agent.\n                -->\n                <propertyName>jacoco.surefireArgLine</propertyName>\n              </configuration>\n            </execution>\n            <!--\n                Ensures that the code coverage report for unit tests is created after\n                unit tests have been run.\n            -->\n            <execution>\n              <id>post-unit-test</id>\n              <phase>test</phase>\n              <goals>\n                <goal>report</goal>\n              </goals>\n            </execution>\n          </executions>\n        </plugin>\n\n        <plugin>\n          <groupId>org.apache.avro</groupId>\n          <artifactId>avro-maven-plugin</artifactId>\n          <version>${avro.version}</version>\n        </plugin>\n\n      </plugins>\n    </pluginManagement>\n\n    <plugins>\n\n      <plugin>\n        <groupId>org.apache.maven.plugins</groupId>\n        <artifactId>maven-toolchains-plugin</artifactId>\n        <version>3.2.0</version>\n        <executions>\n          <execution>\n            <goals>\n              <goal>select-jdk-toolchain</goal>\n            </goals>\n            <configuration>\n              <version>[21,22)</version>\n            </configuration>\n          </execution>\n        </executions>\n      </plugin>\n\n      <plugin>\n        <groupId>org.apache.maven.plugins</groupId>\n        <artifactId>maven-release-plugin</artifactId>\n        <version>3.3.1</version>\n        <dependencies>\n          <dependency>\n            <groupId>nl.basjes.maven.release</groupId>\n            <artifactId>conventional-commits-version-policy</artifactId>\n            <version>1.0.9</version>\n          </dependency>\n        </dependencies>\n        <configuration>\n          <autoVersionSubmodules>true</autoVersionSubmodules>\n\n          <scmCommentPrefix>Release:</scmCommentPrefix>\n          <scmReleaseCommitComment>Release: Version @{releaseLabel}</scmReleaseCommitComment>\n          <scmDevelopmentCommitComment>Release: Start development of next version</scmDevelopmentCommitComment>\n          <scmRollbackCommitComment>Release: Rollback the release of @{releaseLabel}</scmRollbackCommitComment>\n\n          <tagNameFormat>v@{project.version}</tagNameFormat>\n          <projectVersionPolicyId>ConventionalCommitsVersionPolicy</projectVersionPolicyId>\n          <projectVersionPolicyConfig>\n            <versionTag>^v([0-9]+\\.[0-9]+(?:\\.[0-9]+)?)$</versionTag>\n          </projectVersionPolicyConfig>\n\n          <preparationGoals>clean verify</preparationGoals>\n          <releaseProfiles>release,deployToSonatype</releaseProfiles>\n          <pushChanges>false</pushChanges>\n          <remoteTagging>false</remoteTagging>\n        </configuration>\n      </plugin>\n\n      <plugin>\n        <groupId>org.apache.maven.plugins</groupId>\n        <artifactId>maven-enforcer-plugin</artifactId>\n        <executions>\n          <execution>\n            <id>Check build environment requirements</id>\n            <goals>\n              <goal>enforce</goal>\n            </goals>\n            <configuration>\n              <rules>\n                <requireJavaVersion>\n                  <version>[21,)</version>\n                  <message>Need Java 21 or newer to build.</message>\n                </requireJavaVersion>\n                <requireMavenVersion>\n                  <version>[${maven.minimal.version},)</version>\n                  <message>You must use Maven version ${maven.minimal.version} or newer to build this project.</message>\n                  <!-- Reasons for this need:                            -->\n                  <!-- https://issues.apache.org/jira/browse/MDEPLOY-221 -->\n                  <!-- https://issues.apache.org/jira/browse/MNG-6581    -->\n                </requireMavenVersion>\n              </rules>\n            </configuration>\n          </execution>\n\n          <execution>\n            <id>dependency-convergence</id>\n            <phase>${depencency-convergence.phase}</phase>\n            <goals>\n              <goal>enforce</goal>\n            </goals>\n            <configuration>\n              <rules>\n                <dependencyConvergence />\n              </rules>\n            </configuration>\n          </execution>\n\n        </executions>\n      </plugin>\n\n      <plugin>\n        <!-- Description: https://github.com/git-commit-id/git-commit-id-maven-plugin -->\n        <groupId>io.github.git-commit-id</groupId>\n        <artifactId>git-commit-id-maven-plugin</artifactId>\n        <version>10.0.0</version>\n        <executions>\n          <execution>\n            <id>get-the-git-infos</id>\n            <phase>validate</phase>\n            <goals>\n              <goal>revision</goal>\n            </goals>\n          </execution>\n        </executions>\n        <configuration>\n          <dateFormat>yyyy-MM-dd '@' HH:mm:ss z</dateFormat>\n        </configuration>\n      </plugin>\n\n      <plugin>\n        <groupId>org.apache.rat</groupId>\n        <artifactId>apache-rat-plugin</artifactId>\n        <version>${rat.version}</version>\n        <inherited>false</inherited>\n        <!-- https://issues.apache.org/jira/browse/RAT-158 -->\n        <dependencies>\n          <dependency>\n            <groupId>org.apache.maven.doxia</groupId>\n            <artifactId>doxia-core</artifactId>\n            <version>2.1.0</version>\n            <exclusions>\n              <exclusion>\n                <groupId>xerces</groupId>\n                <artifactId>xercesImpl</artifactId>\n              </exclusion>\n            </exclusions>\n          </dependency>\n        </dependencies>\n        <executions>\n          <execution>\n            <phase>test</phase>\n            <goals>\n              <goal>check</goal>\n            </goals>\n          </execution>\n        </executions>\n        <configuration>\n          <consoleOutput>true</consoleOutput>\n          <excludeSubProjects>false</excludeSubProjects>\n          <inputExcludes>\n            <!-- Version control files -->\n            <inputExclude>**/.git/**</inputExclude>\n            <inputExclude>**/.gitignore</inputExclude>\n            <!-- File that cannot be polluted -->\n            <inputExclude>**/.github/FUNDING.yml</inputExclude>\n            <!-- IDE settings and files -->\n            <inputExclude>**/.classpath</inputExclude>\n            <inputExclude>**/.project</inputExclude>\n            <inputExclude>**/.settings/**</inputExclude>\n            <inputExclude>**/.idea/**</inputExclude>\n            <inputExclude>**/*.iml</inputExclude>\n            <inputExclude>**/*.json</inputExclude>\n            <!-- Test and demo input files -->\n            <inputExclude>**/src/test/resources/*.log</inputExclude>\n            <inputExclude>**/demolog/*.log</inputExclude>\n            <inputExclude>GeoIP2-TestData/**</inputExclude>\n            <!-- File generated during build -->\n            <inputExclude>**/target/**</inputExclude>\n            <inputExclude>**/dependency-reduced-pom.xml</inputExclude>\n            <inputExclude>docker/_m2/**</inputExclude>\n            <!-- Documentation and website config -->\n            <inputExclude>**/CNAME</inputExclude>\n            <inputExclude>**/_config.yml</inputExclude>\n          </inputExcludes>\n        </configuration>\n      </plugin>\n\n      <plugin>\n        <groupId>org.apache.maven.plugins</groupId>\n        <artifactId>maven-compiler-plugin</artifactId>\n        <version>3.15.0</version>\n        <configuration>\n          <annotationProcessorPaths>\n            <path>\n              <groupId>org.projectlombok</groupId>\n              <artifactId>lombok</artifactId>\n              <version>${lombok.version}</version>\n            </path>\n          </annotationProcessorPaths>\n        </configuration>\n\n      </plugin>\n\n      <plugin>\n        <groupId>org.apache.maven.plugins</groupId>\n        <artifactId>maven-source-plugin</artifactId>\n        <version>3.4.0</version>\n      </plugin>\n\n    </plugins>\n  </build>\n\n  <modules>\n    <module>devtools</module>\n    <module>parser-core</module>\n    <module>httpdlog</module>\n    <module>examples</module>\n    <module>utils</module>\n  </modules>\n\n  <licenses>\n    <license>\n      <name>Apache License, Version 2.0</name>\n      <url>https://www.apache.org/licenses/LICENSE-2.0.txt</url>\n      <distribution>repo</distribution>\n    </license>\n  </licenses>\n\n  <developers>\n    <developer>\n      <name>Niels Basjes</name>\n      <email>niels@basjes.nl</email>\n      <roles>\n        <role>Architect</role>\n        <role>Developer</role>\n      </roles>\n      <timezone>Europe/Amsterdam</timezone>\n    </developer>\n  </developers>\n\n  <scm>\n    <url>https://github.com/nielsbasjes/logparser</url>\n    <connection>scm:https://github.com/nielsbasjes/logparser.git</connection>\n    <developerConnection>scm:git:file:///${project.basedir}</developerConnection>\n    <tag>HEAD</tag>\n  </scm>\n\n</project>\n"
  },
  {
    "path": "renovate.json",
    "content": "{\n  \"extends\": [\n    \"config:base\",\n    \":semanticCommits\",\n    \":semanticCommitTypeAll(chore)\"\n  ],\n  \"packageRules\": [\n    {\n      \"description\": \"Disable special versions\",\n      \"matchPackagePatterns\": [\"*\"],\n      \"allowedVersions\": \"!/^(?i).*[-_\\\\.](Alpha|Beta|RC|M|EA|Snap|snapshot|jboss|atlassian)[-_\\\\.]?[0-9]?.*$/\"\n    },\n    {\n      \"description\": \"Disable major updates for centos\",\n      \"matchPackageNames\": [\"centos\"],\n      \"matchUpdateTypes\": [\"major\"],\n      \"enabled\": false\n    }\n  ]\n}\n"
  },
  {
    "path": "start-docker.sh",
    "content": "#!/bin/bash\n#\n# Apache HTTPD & NGINX Access log parsing made easy\n# Copyright (C) 2011-2023 Niels Basjes\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nSCRIPTDIR=\"$( cd \"$( dirname \"${BASH_SOURCE[0]}\" )\" >/dev/null && pwd )\"\n\necho \"PWD: ${SCRIPTDIR}\"\n\ncd \"${SCRIPTDIR}\" || ( echo \"This should not be possible\" ; exit 1 )\n\nPROJECTNAME=logparser\nUSER=$(id -un)\nCONTAINER_NAME=${PROJECTNAME}-devtools-${USER}-$$\nDOCKER_BUILD=\"docker build\"\n\nif [ -n \"${INSIDE_DOCKER+x}\" ];\nthen\n  echo \"Nothing to do: You are already INSIDE the docker environment\"\n  exit 1;\nfi\n\nif [[ \"$(docker images -q ${PROJECTNAME}-devtools 2> /dev/null)\" == \"\" ]]; then\n#  DOCKER_BUILD=\"docker build\"\ncat << \"Welcome-message\"\n\n _____      _   _   _                                             _                                      _\n/  ___|    | | | | (_)                                           (_)                                    | |\n\\ `--.  ___| |_| |_ _ _ __   __ _   _   _ _ __     ___ _ ____   ___ _ __ ___  _ __  _ __ ___   ___ _ __ | |_\n `--. \\/ _ \\ __| __| | '_ \\ / _` | | | | | '_ \\   / _ \\ '_ \\ \\ / / | '__/ _ \\| '_ \\| '_ ` _ \\ / _ \\ '_ \\| __|\n/\\__/ /  __/ |_| |_| | | | | (_| | | |_| | |_) | |  __/ | | \\ V /| | | | (_) | | | | | | | | |  __/ | | | |_\n\\____/ \\___|\\__|\\__|_|_| |_|\\__, |  \\__,_| .__/   \\___|_| |_|\\_/ |_|_|  \\___/|_| |_|_| |_| |_|\\___|_| |_|\\__|\n                             __/ |       | |\n                            |___/        |_|\n\n For building Logparser project\n\n This will take a few minutes...\nWelcome-message\nelse\n  DOCKER_BUILD=\"docker build -q\"\n  echo \"Loading LogParser development environment\"\nfi\n\n${DOCKER_BUILD} -t ${PROJECTNAME}-devtools devtools/docker/\n\nbuildStatus=$?\n\nif [ ${buildStatus} -ne 0 ];\nthen\n    echo \"Building the docker image failed.\"\n    exit ${buildStatus}\nfi\n\nif [ \"$(uname -s)\" == \"Linux\" ]; then\n  USER_NAME=${SUDO_USER:=${USER}}\n  USER_ID=$(id -u \"${USER_NAME}\")\n  GROUP_ID=$(id -g \"${USER_NAME}\")\nelse # boot2docker uid and gid\n  USER_NAME=${USER}\n  USER_ID=1000\n  GROUP_ID=50\nfi\n\nEXTRA_DOCKER_STEPS=\"\"\n\nif [ -f \"${HOME}/.gitconfig\" ];\nthen\n  cp \"${HOME}/.gitconfig\" ___git_config_for_docker\n  EXTRA_DOCKER_STEPS=\"ADD ___git_config_for_docker /home/${USER}/.gitconfig\"\nfi\n\nDOCKER_GROUP_ID=$(getent group docker | cut -d':' -f3)\n\ncat - > ___UserSpecificDockerfile << UserSpecificDocker\nFROM ${PROJECTNAME}-devtools\nRUN bash /scripts/configure-for-user.sh \"${USER_NAME}\" \"${USER_ID}\" \"${GROUP_ID}\" \"$(grep -F vboxsf /etc/group)\"\n#RUN groupmod -g ${DOCKER_GROUP_ID} docker\n${EXTRA_DOCKER_STEPS}\nUserSpecificDocker\n\n${DOCKER_BUILD} -t \"${PROJECTNAME}-devtools-${USER_NAME}\" -f ___UserSpecificDockerfile .\n\nbuildStatus=$?\n\nif [ ${buildStatus} -ne 0 ];\nthen\n    echo \"Building the user specific docker image failed.\"\n    exit ${buildStatus}\nfi\n\nrm -f ___git_config_for_docker ___UserSpecificDockerfile\n\necho \"\"\necho \"Docker image build completed.\"\necho \"==============================================================================================\"\necho \"\"\n\n# Do NOT Map the real ~/.m2 directory !!!\n[ -d \"${PWD}/devtools/docker/_m2\"    ] || mkdir \"${PWD}/devtools/docker/_m2\"\n[ -d \"${PWD}/devtools/docker/_gnupg\" ] || mkdir \"${PWD}/devtools/docker/_gnupg\"\n\nMOUNTGPGDIR=\"${PWD}/devtools/docker/_gnupg\"\n\nif [[ \"${1}\" == \"RELEASE\" ]];\nthen\n  cp \"${HOME}\"/.m2/*.xml \"${PWD}/devtools/docker/_m2\"\n  MOUNTGPGDIR=\"${HOME}/.gnupg\"\n\n  echo \"Setting up for release process\"\nfi\n\nDOCKER_INTERACTIVE_RUN=${DOCKER_INTERACTIVE_RUN-\"-i -t\"}\n\nDOCKER_SOCKET_MOUNT=\"\"\nif [ -S /var/run/docker.sock ];\nthen\n  DOCKER_SOCKET_MOUNT=\"-v /var/run/docker.sock:/var/run/docker.sock${V_OPTS:-}\"\n  echo \"Enabling Docker support with the docker build environment.\"\nelse\n  echo \"There is NO Docker support with the docker build environment.\"\nfi\n\nCOMMAND=( \"$@\" )\nif [ $# -eq 0 ];\nthen\n  COMMAND=( \"bash\" \"-i\" )\n  DOCKER_INTERACTIVE=\"-i -t\"\nelse\n  DOCKER_INTERACTIVE=\"-i\"\nfi\n\n# man docker-run\n# When using SELinux, mounted directories may not be accessible\n# to the container. To work around this, with Docker prior to 1.7\n# one needs to run the \"chcon -Rt svirt_sandbox_file_t\" command on\n# the directories. With Docker 1.7 and later the z mount option\n# does this automatically.\n# Since Docker 1.7 was release 5 years ago we only support 1.7 and newer.\nV_OPTS=:z\n\ndocker run --rm=true ${DOCKER_INTERACTIVE}                      \\\n       -u \"${USER_NAME}\"                                        \\\n       -v \"${PWD}:/home/${USER_NAME}/${PROJECTNAME}${V_OPTS:-}\" \\\n       -v \"${HOME}/.m2:/home/${USER_NAME}/.m2${V_OPTS:-}\"       \\\n       -v \"${MOUNTGPGDIR}:/home/${USER_NAME}/.gnupg${V_OPTS:-}\" \\\n       ${DOCKER_SOCKET_MOUNT}                                   \\\n       -w \"/home/${USER}/${PROJECTNAME}\"                        \\\n       -p 1313:1313                                             \\\n       --name \"${CONTAINER_NAME}\"                               \\\n       \"${PROJECTNAME}-devtools-${USER_NAME}\"                   \\\n       \"${COMMAND[@]}\"\n\nexit 0\n"
  },
  {
    "path": "utils/PojoGenerator/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n Apache HTTPD & NGINX Access log parsing made easy\n Copyright (C) 2011-2023 Niels Basjes\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n https://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n-->\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n  <modelVersion>4.0.0</modelVersion>\n\n  <parent>\n    <groupId>nl.basjes.parse.utils</groupId>\n    <artifactId>parse-utils</artifactId>\n    <version>6.0.1-SNAPSHOT</version>\n  </parent>\n\n  <artifactId>PojoGenerator</artifactId>\n\n  <name>Parser - Utils - Pojo Generator</name>\n\n  <dependencies>\n    <dependency>\n      <groupId>args4j</groupId>\n      <artifactId>args4j</artifactId>\n      <version>2.37</version>\n    </dependency>\n    <dependency>\n      <groupId>nl.basjes.parse.httpdlog</groupId>\n      <artifactId>httpdlog-parser</artifactId>\n      <version>${project.version}</version>\n    </dependency>\n    <dependency>\n      <groupId>org.slf4j</groupId>\n      <artifactId>slf4j-simple</artifactId>\n      <version>${slf4j.version}</version>\n    </dependency>\n  </dependencies>\n</project>\n"
  },
  {
    "path": "utils/PojoGenerator/src/main/java/nl/basjes/parse/httpdlog/PojoGenerator.java",
    "content": "/*\n * Apache HTTPD & NGINX Access log parsing made easy\n * Copyright (C) 2011-2023 Niels Basjes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage nl.basjes.parse.httpdlog;\n\nimport nl.basjes.parse.core.Casts;\nimport nl.basjes.parse.core.exceptions.InvalidDissectorException;\nimport nl.basjes.parse.core.exceptions.MissingDissectorsException;\nimport org.kohsuke.args4j.CmdLineException;\nimport org.kohsuke.args4j.CmdLineParser;\nimport org.kohsuke.args4j.Option;\n\nimport java.util.List;\n\npublic class PojoGenerator {\n    @Option(name = \"-logformat\", usage = \"<Apache HTTPD Logformat>\", required = true)\n    private static final String LOG_FORMAT = \"common\";\n\n    static class MyRecord {\n        public void setter(String name, String value) {\n            System.out.println(\"SETTER CALLED FOR \\\"\" + name + \"\\\" = \\\"\" + value + \"\\\"\");\n        }\n    }\n\n    public static void main(String[] args) throws NoSuchMethodException, MissingDissectorsException, InvalidDissectorException {\n        PojoGenerator generator = new PojoGenerator();\n        CmdLineParser parser = new CmdLineParser(generator);\n        try {\n            parser.parseArgument(args);\n            generator.run();\n        } catch (CmdLineException e) {\n            // handling of wrong arguments\n            System.err.println(e.getMessage());\n            parser.printUsage(System.err);\n        }\n    }\n\n    public void run() throws NoSuchMethodException, MissingDissectorsException, InvalidDissectorException {\n        HttpdLoglineParser<MyRecord> parser = new HttpdLoglineParser<>(MyRecord.class, LOG_FORMAT);\n\n        List<String> allPossiblePaths = parser.getPossiblePaths();\n        parser.addParseTarget(MyRecord.class.getMethod(\"setter\", String.class, String.class), allPossiblePaths);\n\n        System.out.println(\"class MyRecord {\\n\");\n\n        for (String field : parser.getPossiblePaths()) {\n            for (Casts cast : parser.getCasts(field)) {\n                System.out.println(\"    @Field{\\\"\" + field + \"\\\"}\\n\" +\n                        \"    public void setter(String name, \" + castToJavaType(cast) + \" value) {\\n\" +\n                        \"        System.out.println(\\\"SETTER CALLED FOR \\\\\\\"\\\" + name + \\\"\\\\\\\" = \\\\\\\"\\\" + value + \\\"\\\\\\\"\\\");\\n\" +\n                        \"    }\\n\");\n            }\n        }\n        System.out.println(\"}\\n\");\n    }\n\n    private String castToJavaType(Casts casts) {\n        switch (casts) {\n            case STRING:\n                return \"String\";\n            case LONG:\n                return \"Long\";\n            case DOUBLE:\n                return \"Double\";\n            default:\n                return null;\n        }\n    }\n\n}\n\n"
  },
  {
    "path": "utils/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n Apache HTTPD & NGINX Access log parsing made easy\n Copyright (C) 2011-2023 Niels Basjes\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n https://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n-->\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n  <modelVersion>4.0.0</modelVersion>\n  <parent>\n    <artifactId>parser-parent</artifactId>\n    <groupId>nl.basjes.parse</groupId>\n    <version>6.0.1-SNAPSHOT</version>\n  </parent>\n\n  <groupId>nl.basjes.parse.utils</groupId>\n  <artifactId>parse-utils</artifactId>\n  <packaging>pom</packaging>\n  <name>Parser - Utils -</name>\n\n  <build>\n    <plugins>\n      <plugin>\n        <groupId>org.apache.maven.plugins</groupId>\n        <artifactId>maven-deploy-plugin</artifactId>\n        <configuration>\n          <skip>true</skip>\n        </configuration>\n      </plugin>\n      <plugin>\n        <groupId>org.apache.maven.plugins</groupId>\n        <artifactId>maven-checkstyle-plugin</artifactId>\n      </plugin>\n      <!-- Disable coverage analysis for this module -->\n      <plugin>\n        <groupId>org.jacoco</groupId>\n        <artifactId>jacoco-maven-plugin</artifactId>\n        <configuration>\n          <skip>true</skip>\n        </configuration>\n      </plugin>\n\n    </plugins>\n  </build>\n\n  <modules>\n    <module>PojoGenerator</module>\n  </modules>\n\n</project>\n"
  }
]