001    package net.sf.logdistiller.logtypes;
002    
003    /*
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     *     http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    
017    import java.io.*;
018    import java.text.ParseException;
019    
020    import net.sf.logdistiller.LogEvent;
021    import net.sf.logdistiller.LogType;
022    import net.sf.logdistiller.util.StringCutter;
023    
024    /**
025     * Log event for commonly used CLF format of web servers, either
026     * <a href="http://httpd.apache.org/docs/2.0/logs.html#common">Common Log Format</a>
027     * or <a href="http://httpd.apache.org/docs/2.0/logs.html#combined">Combined Log Format</a>
028     *
029     * @since 1.1
030     */
031    public class HttpClfLogEvent
032        extends LogEvent
033    {
034        public final static String ID = "http.CLF";
035    
036        public final String host;
037    
038        public final String identity;
039    
040        public final String userid;
041    
042        public final String time;
043    
044        public final String timeDate;
045    
046        public final String timeTime;
047    
048        public final String request;
049    
050        public final String requestMethod;
051    
052        public final String requestUri;
053    
054        public final String requestProtocol;
055    
056        public final String status;
057    
058        public final String size;
059    
060        public final String referer;
061    
062        public final String userAgent;
063    
064        public final String userAgentName;
065    
066        public final String userAgentVersion;
067    
068        public final String userAgentDetails;
069    
070        public final String other;
071    
072        public final static LogType LOGTYPE = new LogType.Basic( ID );
073    
074        private final static String[] ATTRIBUTE_NAMES = { "logSource", "host", "identity", "userid",
075            "time", "time.date", "time.time",
076            "request", "request.method", "request.uri", "request.protocol",
077            "status", "size", "referer",
078            "user-agent", "user-agent.name", "user-agent.version", "user-agent.details",
079            "other" };
080    
081        public final static LogType.Description DESCRIPTION = new Description( (LogType.Basic) LOGTYPE, ATTRIBUTE_NAMES );
082    
083        public HttpClfLogEvent( Factory factory, String rawLog )
084            throws ParseException
085        {
086            super( factory, rawLog );
087            StringCutter sc = new StringCutter( rawLog );
088            host = sc.parseTo( " " );
089            identity = sc.parseTo( " " );
090            userid = sc.parseTo( " [" );
091            time = sc.parseTo( "] \"" );
092            timeDate = time.substring( 0, 11 );
093            timeTime = time.substring( 12, 20 );
094            request = sc.parseTo( "\" " );
095            StringCutter req = new StringCutter( request );
096            requestMethod = req.parseTo( " " );
097            requestUri = req.parseTo( " " );
098            requestProtocol = req.getRemaining();
099            status = sc.parseTo( " " );
100            size = sc.parseTo( " \"", false );
101            if ( sc.getRemaining().length() == 0 )
102            { // Common Log Format only
103                referer = userAgent = userAgentName = userAgentVersion = userAgentDetails = other = "";
104            }
105            else
106            { // Combined Log Format
107                referer = sc.parseTo( "\" \"" );
108                userAgent = sc.parseTo( "\"" );
109                StringCutter ua = new StringCutter( userAgent );
110                userAgentName = ua.parseTo( "/", false );
111                userAgentVersion = ua.parseTo( " ", false );
112                ua.parseTo( "(", false );
113                userAgentDetails = ua.parseTo( ")", false );
114                other = sc.getRemaining().trim();
115            }
116            setAttributes( new String[] { factory.getLogSource(), host, identity, userid,
117                time, timeDate, timeTime,
118                request, requestMethod, requestUri, requestProtocol,
119                status, size, referer,
120                userAgent, userAgentName, userAgentVersion, userAgentDetails,
121                other } );
122        }
123    
124        private static class Description
125            extends LogType.Description
126        {
127            public Description( LogType.Basic logtype, String[] attributeNames )
128            {
129                super( logtype, attributeNames );
130                logtype.setDescription( this );
131            }
132    
133            public LogEvent.Factory newFactory( Reader reader, String logSource )
134                throws IOException
135            {
136                return new Factory( this, reader, logSource );
137            }
138    
139            public int getTimestampAttribute()
140            {
141                return 3;
142            }
143    
144            public String getDefaultSamplingAttributes()
145            {
146                return "request.uri,user-agent";
147            }
148        }
149    
150        private static class Factory
151            extends LogEvent.Factory
152        {
153            private final LineNumberReader reader;
154    
155            public Factory( Description description, Reader reader, String logSource )
156                throws FileNotFoundException
157            {
158                super( description, logSource );
159                this.reader = new LineNumberReader( reader );
160            }
161    
162            protected LogEvent readNextEvent()
163                throws IOException, ParseException
164            {
165                String curLine = reader.readLine();
166                if ( curLine == null )
167                {
168                    // EOF
169                    return null;
170                }
171                try
172                {
173                    return new HttpClfLogEvent( this, curLine );
174                }
175                catch ( RuntimeException re )
176                {
177                    throw new RuntimeException( "error while parsing line '" + curLine + "'", re );
178                }
179            }
180        }
181    }