/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.hadoop.hdfs; import java.io.IOException; import java.io.OutputStream; import java.net.URI; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.*; import org.apache.hadoop.fs.permission.FsPermission; import org.apache.hadoop.hdfs.DistributedFileSystem; import org.apache.hadoop.metrics.APITrace; import org.apache.hadoop.metrics.APITrace.StreamTracer; import org.apache.hadoop.metrics.APITrace.TraceableStream; import org.apache.hadoop.util.Progressable; import org.apache.hadoop.util.ReflectionUtils; /** * Tracing class for recording client-side API calls and RPC calls. * * We override the methods in FilterFileSystem which are interesting to trace. In general, we * make an identical unmodified call to the underlying file system, log the method call and * results, and return the value that the underlying file system returned unmodified. The * only exceptions are the calls which return streams (e.g., open or create). For these calls, * we want to record I/O that is performed on streams, so we return special trace wrappers # around the streams returned returned by the underlying file system. */ public class APITraceFileSystem extends FilterFileSystem { private static final String[] traceConfOpts = {"mapred.job.id", "mapred.task.id", "fs.default.name"}; APITraceFileSystem() throws IOException { this.fs = null; } APITraceFileSystem(FileSystem fs) throws IOException { super(fs); } /* * Initialize an API-Trace File System */ public void initialize(URI name, Configuration conf) throws IOException { // if an underlying fs has not already been specified, // create and initialize one if (this.fs == null) { Class<?> clazz = conf.getClass("fs.apitrace.underlyingfs.impl", DistributedFileSystem.class); if (clazz == null) { throw new IOException("No FileSystem for fs.apitrace.underlyingfs.impl."); } this.fs = (FileSystem)ReflectionUtils.newInstance(clazz, null); super.initialize(name, conf); } // trace conf options for (String opt : traceConfOpts) { APITrace.CallEvent ce = new APITrace.CallEvent(); ce.logCall(APITrace.COMMENT_msg, null, new Object[] {opt, conf.get(opt, "")}); } } public FileSystem getRawFileSystem() { // TODO: I'm not sure if this is the correct behavior. if (fs instanceof FilterFileSystem) { return ((FilterFileSystem)fs).getRawFileSystem(); } return fs; } // start auto(wrap:FileSystem) public String getName() { APITrace.CallEvent ce; String rv; ce = new APITrace.CallEvent(); rv = super.getName(); ce.logCall(APITrace.CALL_getName, rv, null); return rv; } public BlockLocation[] getFileBlockLocations(FileStatus file, long start, long len) throws IOException { APITrace.CallEvent ce; BlockLocation[] rv; ce = new APITrace.CallEvent(); rv = super.getFileBlockLocations(file, start, len); ce.logCall(APITrace.CALL_getFileBlockLocations, null, new Object[] { start, len}); return rv; } public FSDataInputStream open(Path f, int bufferSize) throws IOException { APITrace.CallEvent ce; FSDataInputStream rv; ce = new APITrace.CallEvent(); rv = super.open(f, bufferSize); rv = new TraceFSDataInputStream(rv, new StreamTracer()); ce.logCall(APITrace.CALL_open, rv, new Object[] { f, bufferSize}); return rv; } public FSDataOutputStream append(Path f, int bufferSize, Progressable progress) throws IOException { APITrace.CallEvent ce; FSDataOutputStream rv; ce = new APITrace.CallEvent(); rv = super.append(f, bufferSize, progress); rv = new TraceFSDataOutputStream(rv, new StreamTracer()); ce.logCall(APITrace.CALL_append, rv, new Object[] { f, bufferSize}); return rv; } public FSDataOutputStream create(Path f, FsPermission permission, boolean overwrite, int bufferSize, short replication, long blockSize, Progressable progress) throws IOException { APITrace.CallEvent ce; FSDataOutputStream rv; ce = new APITrace.CallEvent(); rv = super.create(f, permission, overwrite, bufferSize, replication, blockSize, progress); rv = new TraceFSDataOutputStream(rv, new StreamTracer()); ce.logCall(APITrace.CALL_create, rv, new Object[] { f, overwrite, bufferSize, replication, blockSize}); return rv; } public FSDataOutputStream create(Path f, FsPermission permission, boolean overwrite, int bufferSize, short replication, long blockSize, int bytesPerChecksum, Progressable progress, boolean forceSync) throws IOException { APITrace.CallEvent ce; FSDataOutputStream rv; ce = new APITrace.CallEvent(); rv = super.create(f, permission, overwrite, bufferSize, replication, blockSize, bytesPerChecksum, progress, forceSync); rv = new TraceFSDataOutputStream(rv, new StreamTracer()); ce.logCall(APITrace.CALL_create1, rv, new Object[] { f, overwrite, bufferSize, replication, blockSize, bytesPerChecksum, forceSync}); return rv; } @Deprecated public FSDataOutputStream createNonRecursive(Path f, boolean overwrite, int bufferSize, short replication, long blockSize, Progressable progress, boolean forceSync) throws IOException { APITrace.CallEvent ce; FSDataOutputStream rv; ce = new APITrace.CallEvent(); rv = super.createNonRecursive(f, overwrite, bufferSize, replication, blockSize, progress, forceSync); rv = new TraceFSDataOutputStream(rv, new StreamTracer()); ce.logCall(APITrace.CALL_createNonRecursive, rv, new Object[] { f, overwrite, bufferSize, replication, blockSize, forceSync}); return rv; } @Deprecated public FSDataOutputStream createNonRecursive(Path f, FsPermission permission, boolean overwrite, int bufferSize, short replication, long blockSize, Progressable progress, boolean forceSync, boolean doParallelWrite) throws IOException { APITrace.CallEvent ce; FSDataOutputStream rv; ce = new APITrace.CallEvent(); rv = super.createNonRecursive(f, permission, overwrite, bufferSize, replication, blockSize, progress, forceSync, doParallelWrite); rv = new TraceFSDataOutputStream(rv, new StreamTracer()); ce.logCall(APITrace.CALL_createNonRecursive1, rv, new Object[] { f, overwrite, bufferSize, replication, blockSize, forceSync, doParallelWrite}); return rv; } public boolean setReplication(Path src, short replication) throws IOException { APITrace.CallEvent ce; boolean rv; ce = new APITrace.CallEvent(); rv = super.setReplication(src, replication); ce.logCall(APITrace.CALL_setReplication, rv, new Object[] { src, replication}); return rv; } public boolean hardLink(Path src, Path dst) throws IOException { APITrace.CallEvent ce; boolean rv; ce = new APITrace.CallEvent(); rv = super.hardLink(src, dst); ce.logCall(APITrace.CALL_hardLink, rv, new Object[] { src, dst}); return rv; } public boolean rename(Path src, Path dst) throws IOException { APITrace.CallEvent ce; boolean rv; ce = new APITrace.CallEvent(); rv = super.rename(src, dst); ce.logCall(APITrace.CALL_rename, rv, new Object[] { src, dst}); return rv; } public boolean delete(Path f) throws IOException { APITrace.CallEvent ce; boolean rv; ce = new APITrace.CallEvent(); rv = super.delete(f); ce.logCall(APITrace.CALL_delete, rv, new Object[] { f}); return rv; } public boolean delete(Path f, boolean recursive) throws IOException { APITrace.CallEvent ce; boolean rv; ce = new APITrace.CallEvent(); rv = super.delete(f, recursive); ce.logCall(APITrace.CALL_delete1, rv, new Object[] { f, recursive}); return rv; } public FileStatus[] listStatus(Path f) throws IOException { APITrace.CallEvent ce; FileStatus[] rv; ce = new APITrace.CallEvent(); rv = super.listStatus(f); ce.logCall(APITrace.CALL_listStatus, rv, new Object[] { f}); return rv; } public boolean mkdirs(Path f, FsPermission permission) throws IOException { APITrace.CallEvent ce; boolean rv; ce = new APITrace.CallEvent(); rv = super.mkdirs(f, permission); ce.logCall(APITrace.CALL_mkdirs, rv, new Object[] { f}); return rv; } public OpenFileInfo[] iterativeGetOpenFiles(Path prefix, int millis, String start) throws IOException { APITrace.CallEvent ce; OpenFileInfo[] rv; ce = new APITrace.CallEvent(); rv = super.iterativeGetOpenFiles(prefix, millis, start); ce.logCall(APITrace.CALL_iterativeGetOpenFiles, null, new Object[] { prefix, millis, start}); return rv; } public long getUsed() throws IOException{ APITrace.CallEvent ce; long rv; ce = new APITrace.CallEvent(); rv = super.getUsed(); ce.logCall(APITrace.CALL_getUsed, rv, null); return rv; } public long getDefaultBlockSize() { APITrace.CallEvent ce; long rv; ce = new APITrace.CallEvent(); rv = super.getDefaultBlockSize(); ce.logCall(APITrace.CALL_getDefaultBlockSize, rv, null); return rv; } public short getDefaultReplication() { APITrace.CallEvent ce; short rv; ce = new APITrace.CallEvent(); rv = super.getDefaultReplication(); ce.logCall(APITrace.CALL_getDefaultReplication, rv, null); return rv; } public ContentSummary getContentSummary(Path f) throws IOException { APITrace.CallEvent ce; ContentSummary rv; ce = new APITrace.CallEvent(); rv = super.getContentSummary(f); ce.logCall(APITrace.CALL_getContentSummary, null, new Object[] { f}); return rv; } public FileStatus getFileStatus(Path f) throws IOException { APITrace.CallEvent ce; FileStatus rv; ce = new APITrace.CallEvent(); rv = super.getFileStatus(f); ce.logCall(APITrace.CALL_getFileStatus, rv, new Object[] { f}); return rv; } public FileChecksum getFileChecksum(Path f) throws IOException { APITrace.CallEvent ce; FileChecksum rv; ce = new APITrace.CallEvent(); rv = super.getFileChecksum(f); ce.logCall(APITrace.CALL_getFileChecksum, null, new Object[] { f}); return rv; } public void setVerifyChecksum(boolean verifyChecksum) { APITrace.CallEvent ce; ce = new APITrace.CallEvent(); super.setVerifyChecksum(verifyChecksum); ce.logCall(APITrace.CALL_setVerifyChecksum, null, new Object[] { verifyChecksum}); } public void setOwner(Path p, String username, String groupname) throws IOException { APITrace.CallEvent ce; ce = new APITrace.CallEvent(); super.setOwner(p, username, groupname); ce.logCall(APITrace.CALL_setOwner, null, new Object[] { p, username, groupname}); } public void setTimes(Path p, long mtime, long atime) throws IOException { APITrace.CallEvent ce; ce = new APITrace.CallEvent(); super.setTimes(p, mtime, atime); ce.logCall(APITrace.CALL_setTimes, null, new Object[] { p, mtime, atime}); } public void setPermission(Path p, FsPermission permission) throws IOException { APITrace.CallEvent ce; ce = new APITrace.CallEvent(); super.setPermission(p, permission); ce.logCall(APITrace.CALL_setPermission, null, new Object[] { p}); } // end auto /* * Tracer for output streams returned by FileSystem.create(). * * The underlying file system will return an FSDataOutputStream object upon create. * Rather than adding log statements to the logic of every FSDataOutputStream implementation we * might want to trace, we simply return a wrapper around the stream that the underlying FS * returned. Our design is guided by three desirable properties for the wrapper: * * (1) The wrapper must implement the FSDataOutputStream interface (requirement). * (2) The wrapper should have a relatively simple tracing API (desirable). * (3) The wrapper should never silently perform I/O without logging, * even if parent classes support new methods in the future (desirable). * * In order to satisfy all three constraints, we need to provide two levels of wrappers * over the object returned by the underlying FS. In order to satisfy constraint * (1), we return our own implementation of FSDataOutputStream as the higher-level wrapper. * * Unfortunately, this wrapper alone is not sufficient as it does not meet requirement (2): * FSDataOutputStream inherits a broad API from java.io.DataOutputStream (e.g., we would need * to trace writeBoolean, writeFloat, writeUTF, etc if we traced at this level). Also, * simply tracing at the FSDataOutputStream level would violate (3), as DataOutputStreams * automatically wrap an inner stream, so any new methods added to java.io.DataOutputStream * would be inherited by our implementation, meaning that I/O might occur without tracing. * * In order to satisfy (2) and (3), we use an implementation of java.io.OutputStream as our * lower-level wrapper. This level performs all the necessary logging calls. */ private static class TraceFSDataOutputStream extends FSDataOutputStream implements TraceableStream { private StreamTracer streamTracer; private static class TraceFSOutputStream extends OutputStream implements TraceableStream, Syncable { private FSDataOutputStream out; private StreamTracer streamTracer; TraceFSOutputStream(FSDataOutputStream out, StreamTracer streamTracer) { this.out = out; this.streamTracer = streamTracer; } public StreamTracer getStreamTracer() { return streamTracer; } // start auto(wrap:FSOutputStream) public void sync() throws IOException { APITrace.CallEvent ce; ce = new APITrace.CallEvent(); out.sync(); streamTracer.logCall(ce, APITrace.CALL_sync, null, null); } public void write(int b) throws IOException { APITrace.CallEvent ce; ce = new APITrace.CallEvent(); out.write(b); streamTracer.logIOCall(ce, 1); } public void write(byte[] b) throws IOException { APITrace.CallEvent ce; ce = new APITrace.CallEvent(); out.write(b); streamTracer.logIOCall(ce, b.length); } public void write(byte[] b, int off, int len) throws IOException { APITrace.CallEvent ce; ce = new APITrace.CallEvent(); out.write(b, off, len); streamTracer.logIOCall(ce, len); } public void flush() throws IOException { APITrace.CallEvent ce; ce = new APITrace.CallEvent(); out.flush(); streamTracer.logCall(ce, APITrace.CALL_flush, null, null); } public void close() throws IOException { APITrace.CallEvent ce; ce = new APITrace.CallEvent(); out.close(); streamTracer.logCall(ce, APITrace.CALL_close2, null, null); } // end auto } public TraceFSDataOutputStream(FSDataOutputStream out, StreamTracer streamTracer) throws IOException { super(new TraceFSOutputStream(out, streamTracer), null, out.getPos()); this.streamTracer = streamTracer; } public StreamTracer getStreamTracer() { return streamTracer; } } /* * Tracer for input streams returned by FileSystem.open(). * * The underlying file system will return an FSDataInputStream object upon open. * Rather than adding log statements to the logic of every FSDataInputStream implementation we * might want to trace, we simply return a wrapper around the stream that the underlying FS * returned. Our design is guided by three desirable properties for the wrapper: * * (1) The wrapper must implement the FSDataInputStream interface (requirement). * (2) The wrapper should have a relatively simple tracing API (desirable). * (3) The wrapper should never silently perform I/O without logging, * even if parent classes support new methods in the future (desirable). * * In order to satisfy all three constraints, we need to provide two levels of wrappers * over the object returned by the underlying FS. In order to satisfy constraint * (1), we return our own implementation of FSDataInputStream as the higher-level wrapper. * * Unfortunately, this wrapper alone is not sufficient as it does not meet requirement (2): * FSDataInputStream inherits a broad API from java.io.DataInputStream (e.g., we would need * to trace readBoolean, readFloat, readUTF, etc if we traced at this level). Also, * simply tracing at the FSDataInputStream level would violate (3), as DataInputStreams * automatically wrap an inner stream, so any new methods added to java.io.DataInputStream * would be inherited by our implementation, meaning that I/O might occur without tracing. * * In order to satisfy (2) and (3), we use an implementation of hadoop.fs.FSInputStream as * our lower-level wrapper. This level performs all the necessary logging calls. */ private static class TraceFSDataInputStream extends FSDataInputStream implements TraceableStream { private StreamTracer streamTracer = null; private static class TraceFSInputStream extends FSInputStream implements TraceableStream { private FSDataInputStream in; private StreamTracer streamTracer = null; TraceFSInputStream(FSDataInputStream in, StreamTracer streamTracer) { this.in = in; this.streamTracer = streamTracer; } public StreamTracer getStreamTracer() { return streamTracer; } // start auto(wrap:FSInputStream) public void seek(long pos) throws IOException { APITrace.CallEvent ce; ce = new APITrace.CallEvent(); in.seek(pos); streamTracer.logCall(ce, APITrace.CALL_seek, null, new Object[] { pos}); } public long getPos() throws IOException { return in.getPos(); } public boolean seekToNewSource(long targetPos) throws IOException { APITrace.CallEvent ce; boolean rv; ce = new APITrace.CallEvent(); rv = in.seekToNewSource(targetPos); streamTracer.logCall(ce, APITrace.CALL_seekToNewSource, rv, new Object[] { targetPos}); return rv; } public int read(long position, byte[] buffer, int offset, int length) throws IOException { APITrace.CallEvent ce; int rv; ce = new APITrace.CallEvent(); rv = in.read(position, buffer, offset, length); streamTracer.logCall(ce, APITrace.CALL_read, rv, new Object[] { position, offset, length}); return rv; } public void readFully(long position, byte[] buffer, int offset, int length) throws IOException { APITrace.CallEvent ce; ce = new APITrace.CallEvent(); in.readFully(position, buffer, offset, length); streamTracer.logCall(ce, APITrace.CALL_readFully, null, new Object[] { position, offset, length}); } public void readFully(long position, byte[] buffer) throws IOException { APITrace.CallEvent ce; ce = new APITrace.CallEvent(); in.readFully(position, buffer); streamTracer.logCall(ce, APITrace.CALL_readFully1, null, new Object[] { position}); } public int read() throws IOException { APITrace.CallEvent ce; int rv; ce = new APITrace.CallEvent(); rv = in.read(); if (rv >= 0) { streamTracer.logIOCall(ce, 1); } else { streamTracer.logCall(ce, APITrace.CALL_read1, rv, null); } return rv; } public int read(byte[] b) throws IOException { APITrace.CallEvent ce; int rv; ce = new APITrace.CallEvent(); rv = in.read(b); if (rv >= 0) { streamTracer.logIOCall(ce, rv); } else { streamTracer.logCall(ce, APITrace.CALL_read2, rv, null); } return rv; } public int read(byte[] b, int off, int len) throws IOException { APITrace.CallEvent ce; int rv; ce = new APITrace.CallEvent(); rv = in.read(b, off, len); if (rv >= 0) { streamTracer.logIOCall(ce, rv); } else { streamTracer.logCall(ce, APITrace.CALL_read3, rv, new Object[] { off, len}); } return rv; } public long skip(long n) throws IOException { APITrace.CallEvent ce; long rv; ce = new APITrace.CallEvent(); rv = in.skip(n); streamTracer.logCall(ce, APITrace.CALL_skip, rv, new Object[] { n}); return rv; } public int available() throws IOException { APITrace.CallEvent ce; int rv; ce = new APITrace.CallEvent(); rv = in.available(); streamTracer.logCall(ce, APITrace.CALL_available, rv, null); return rv; } public void close() throws IOException { APITrace.CallEvent ce; ce = new APITrace.CallEvent(); in.close(); streamTracer.logCall(ce, APITrace.CALL_close1, null, null); } public void mark(int readlimit) { APITrace.CallEvent ce; ce = new APITrace.CallEvent(); in.mark(readlimit); streamTracer.logCall(ce, APITrace.CALL_mark, null, new Object[] { readlimit}); } public void reset() throws IOException { APITrace.CallEvent ce; ce = new APITrace.CallEvent(); in.reset(); streamTracer.logCall(ce, APITrace.CALL_reset, null, null); } // end auto } public TraceFSDataInputStream(FSDataInputStream in, StreamTracer streamTracer) throws IOException { super(new TraceFSInputStream(in, streamTracer)); this.streamTracer = streamTracer; } public StreamTracer getStreamTracer() { return streamTracer; } } }